{"id":295,"date":"2023-10-20T13:18:36","date_gmt":"2023-10-20T05:18:36","guid":{"rendered":"https:\/\/miie.net\/?p=295"},"modified":"2023-10-20T13:18:36","modified_gmt":"2023-10-20T05:18:36","slug":"pro-c10-chapter-5-understanding-encapsulation","status":"publish","type":"post","link":"https:\/\/diji.net\/?p=295","title":{"rendered":"Pro C#10 CHAPTER 5 Understanding Encapsulation"},"content":{"rendered":"<h2>PART III Object Oriented Programming with C<\/h2>\n<p>\u7b2c\u4e09\u90e8\u5206 \u4f7f\u7528 C \u8fdb\u884c\u9762\u5411\u5bf9\u8c61\u7f16\u7a0b#<\/p>\n<h2>CHAPTER 5 Understanding Encapsulation<\/h2>\n<p>\u4e86\u89e3\u5c01\u88c5<\/p>\n<p>In Chapters 3 and 4, you investigated a number of core syntactical constructs that are commonplace to any .NET Core application you might be developing. Here, you will begin your examination of the object-oriented capabilities of C#. The first order of business is to examine the process of building<br \/>\nwell-defined class types that support any number of constructors. After you understand the basics of defining classes and allocating objects, the remainder of this chapter will examine the role of encapsulation. Along the way, you will learn how to define class properties and come to understand the details of the static keyword, object initialization syntax, read-only fields, constant data, and partial classes.<br \/>\n\u5728\u7b2c 3 \u7ae0\u548c\u7b2c 4 \u7ae0\u4e2d\uff0c\u60a8\u7814\u7a76\u4e86\u60a8\u53ef\u80fd\u6b63\u5728\u5f00\u53d1\u7684\u4efb\u4f55 .NET Core \u5e94\u7528\u7a0b\u5e8f\u5e38\u89c1\u7684\u4e00\u4e9b\u6838\u5fc3\u8bed\u6cd5\u7ed3\u6784\u3002\u5728\u8fd9\u91cc\uff0c\u60a8\u5c06\u5f00\u59cb\u7814\u7a76 C# \u7684\u9762\u5411\u5bf9\u8c61\u529f\u80fd\u3002\u9996\u8981\u4efb\u52a1\u662f\u68c0\u67e5\u6784\u5efa\u8fc7\u7a0b\u652f\u6301\u4efb\u610f\u6570\u91cf\u7684\u6784\u9020\u51fd\u6570\u7684\u660e\u786e\u5b9a\u4e49\u7684\u7c7b\u7c7b\u578b\u3002\u4e86\u89e3\u5b9a\u4e49\u7c7b\u548c\u5206\u914d\u5bf9\u8c61\u7684\u57fa\u7840\u77e5\u8bc6\u540e\uff0c\u672c\u7ae0\u7684\u5176\u4f59\u90e8\u5206\u5c06\u7814\u7a76\u5c01\u88c5\u7684\u4f5c\u7528\u3002\u5728\u6b64\u8fc7\u7a0b\u4e2d\uff0c\u60a8\u5c06\u5b66\u4e60\u5982\u4f55\u5b9a\u4e49\u7c7b\u5c5e\u6027\uff0c\u5e76\u4e86\u89e3\u9759\u6001\u5173\u952e\u5b57\u3001\u5bf9\u8c61\u521d\u59cb\u5316\u8bed\u6cd5\u3001\u53ea\u8bfb\u5b57\u6bb5\u3001\u5e38\u91cf\u6570\u636e\u548c\u5206\u90e8\u7c7b\u7684\u8be6\u7ec6\u4fe1\u606f\u3002<\/p>\n<h3>Introducing the C# Class Type<\/h3>\n<p>\u4ecb\u7ecd C# \u7c7b\u7c7b\u578b<br \/>\nAs far as the .NET platform is concerned, one of the most fundamental programming constructs is the class type. Formally, a class is a user-defined type that is composed of field data (often called member variables) and members that operate on this data (such as constructors, properties, methods, events, etc.). Collectively, the set of field data represents the \u201cstate\u201d of a class instance (otherwise known as an object). The power of object-oriented languages, such as C#, is that by grouping data and related functionality in a unified class definition, you are able to model your software after entities in the real world.<br \/>\n\u5c31 .NET \u5e73\u53f0\u800c\u8a00\uff0c\u6700\u57fa\u672c\u7684\u7f16\u7a0b\u6784\u9020\u4e4b\u4e00\u662f\u7c7b\u7c7b\u578b\u3002\u4ece\u5f62\u5f0f\u4e0a\u8bb2\uff0c\u7c7b\u662f\u4e00\u79cd\u7528\u6237\u5b9a\u4e49\u7684\u7c7b\u578b\uff0c\u7531\u5b57\u6bb5\u6570\u636e\uff08\u901a\u5e38\u79f0\u4e3a\u6210\u5458\u53d8\u91cf\uff09\u548c\u5bf9\u6b64\u6570\u636e\u8fdb\u884c\u64cd\u4f5c\u7684\u6210\u5458\uff08\u5982\u6784\u9020\u51fd\u6570\u3001\u5c5e\u6027\u3001\u65b9\u6cd5\u3001\u4e8b\u4ef6\u7b49\uff09\u7ec4\u6210\u3002\u603b\u7684\u6765\u8bf4\uff0c\u5b57\u6bb5\u6570\u636e\u96c6\u8868\u793a\u7c7b\u5b9e\u4f8b\uff08\u4e5f\u79f0\u4e3a\u5bf9\u8c61\uff09\u7684\u201c\u72b6\u6001\u201d\u3002\u9762\u5411\u5bf9\u8c61\u8bed\u8a00\uff08\u5982 C#\uff09\u7684\u5f3a\u5927\u4e4b\u5904\u5728\u4e8e\uff0c\u901a\u8fc7\u5728\u7edf\u4e00\u7684\u7c7b\u5b9a\u4e49\u4e2d\u5bf9\u6570\u636e\u548c\u76f8\u5173\u529f\u80fd\u8fdb\u884c\u5206\u7ec4\uff0c\u60a8\u53ef\u4ee5\u6839\u636e\u73b0\u5b9e\u4e16\u754c\u4e2d\u7684\u5b9e\u4f53\u5bf9\u8f6f\u4ef6\u8fdb\u884c\u5efa\u6a21\u3002<\/p>\n<p>To get the ball rolling, create a new C# Console Application project named SimpleClassExample. Next, insert a new class file (named Car.cs) into your project. In this new file, add the following file-scoped namespace:<br \/>\n\u82e5\u8981\u4f7f\u7403\u6eda\u52a8\uff0c\u8bf7\u521b\u5efa\u4e00\u4e2a\u540d\u4e3a SimpleClassExample \u7684\u65b0 C# \u63a7\u5236\u53f0\u5e94\u7528\u7a0b\u5e8f\u9879\u76ee\u3002\u63a5\u4e0b\u6765\uff0c\u5c06\u4e00\u4e2a\u65b0\u7684\u7c7b\u6587\u4ef6\uff08\u540d\u4e3a Car.cs\uff09\u63d2\u5165\u5230\u9879\u76ee\u4e2d\u3002\u5728\u6b64\u65b0\u6587\u4ef6\u4e2d\uff0c\u6dfb\u52a0\u4ee5\u4e0b\u6587\u4ef6\u8303\u56f4\u7684\u547d\u540d\u7a7a\u95f4\uff1a<\/p>\n<p>namespace SimpleClassExample;<br \/>\nA class is defined in C# using the class keyword. Here is the simplest possible declaration (make sure to add the class declaration after the SimpleClassExample namespace):<br \/>\nclass Car<br \/>\n{<br \/>\n}<\/p>\n<p>After you have defined a class type, you will need to consider the set of member variables that will be used to represent its state. For example, you might decide that cars maintain an int data type to represent the current speed and a string data type to represent the car\u2019s friendly pet name. Given these initial design notes, update your Car class as follows:<br \/>\n\u5b9a\u4e49\u7c7b\u7c7b\u578b\u540e\uff0c\u9700\u8981\u8003\u8651\u5c06\u7528\u4e8e\u8868\u793a\u5176\u72b6\u6001\u7684\u6210\u5458\u53d8\u91cf\u96c6\u3002\u4f8b\u5982\uff0c\u60a8\u53ef\u80fd\u51b3\u5b9a\u6c7d\u8f66\u7ef4\u62a4\u4e00\u4e2a int \u6570\u636e\u7c7b\u578b\u6765\u8868\u793a\u5f53\u524d\u901f\u5ea6\uff0c\u5e76\u4f7f\u7528\u4e00\u4e2a\u5b57\u7b26\u4e32\u6570\u636e\u7c7b\u578b\u6765\u8868\u793a\u6c7d\u8f66\u7684\u53cb\u597d\u5ba0\u7269\u540d\u79f0\u3002\u6839\u636e\u8fd9\u4e9b\u521d\u59cb\u8bbe\u8ba1\u8bf4\u660e\uff0c\u6309\u5982\u4e0b\u65b9\u5f0f\u66f4\u65b0\u60a8\u7684 Car \u7c7b\uff1a<\/p>\n<p>class Car<br \/>\n{<br \/>\n\/\/ The 'state' of the Car. public string petName; public int currSpeed;<br \/>\n}<\/p>\n<p>Notice that these member variables are declared using the public access modifier. Public members of a class are directly accessible once an object of this type has been created. Recall the term object is used to describe an instance of a given class type created using the new keyword.<br \/>\n\u8bf7\u6ce8\u610f\uff0c\u8fd9\u4e9b\u6210\u5458\u53d8\u91cf\u662f\u4f7f\u7528\u516c\u5171\u8bbf\u95ee\u4fee\u9970\u7b26\u58f0\u660e\u7684\u3002\u521b\u5efa\u6b64\u7c7b\u578b\u7684\u5bf9\u8c61\u540e\uff0c\u53ef\u4ee5\u76f4\u63a5\u8bbf\u95ee\u7c7b\u7684\u516c\u5171\u6210\u5458\u3002\u56de\u60f3\u4e00\u4e0b\uff0c\u672f\u8bed\u5bf9\u8c61\u7528\u4e8e\u63cf\u8ff0\u4f7f\u7528 new \u5173\u952e\u5b57\u521b\u5efa\u7684\u7ed9\u5b9a\u7c7b\u7c7b\u578b\u7684\u5b9e\u4f8b\u3002<\/p>\n<p>\u25a0 Note  Field data of a class should seldom (if ever) be defined as public. To preserve the integrity of your state data, it is a far better design to define data as private (or possibly protected) and allow controlled access to the data via properties (as shown later in this chapter). However, to keep this first example as simple as possible, public data fits the bill.<br \/>\n\u8bf7\u6ce8\u610f\uff0c\u8fd9\u4e9b\u6210\u5458\u53d8\u91cf\u662f\u4f7f\u7528\u516c\u5171\u8bbf\u95ee\u4fee\u9970\u7b26\u58f0\u660e\u7684\u3002\u521b\u5efa\u6b64\u7c7b\u578b\u7684\u5bf9\u8c61\u540e\uff0c\u53ef\u4ee5\u76f4\u63a5\u8bbf\u95ee\u7c7b\u7684\u516c\u5171\u6210\u5458\u3002\u56de\u60f3\u4e00\u4e0b\uff0c\u672f\u8bed\u5bf9\u8c61\u7528\u4e8e\u63cf\u8ff0\u4f7f\u7528 new \u5173\u952e\u5b57\u521b\u5efa\u7684\u7ed9\u5b9a\u7c7b\u7c7b\u578b\u7684\u5b9e\u4f8b\u3002<\/p>\n<p>After you have defined the set of member variables representing the state of the class, the next design step is to establish the members that model its behavior. For this example, the Car class will define one method named SpeedUp() and another named PrintState(). Update your class as so:<br \/>\n\u5b9a\u4e49\u8868\u793a\u7c7b\u72b6\u6001\u7684\u6210\u5458\u53d8\u91cf\u96c6\u540e\uff0c\u4e0b\u4e00\u4e2a\u8bbe\u8ba1\u6b65\u9aa4\u662f\u5efa\u7acb\u5bf9\u5176\u884c\u4e3a\u8fdb\u884c\u5efa\u6a21\u7684\u6210\u5458\u3002\u5bf9\u4e8e\u6b64\u793a\u4f8b\uff0cCar \u7c7b\u5c06\u5b9a\u4e49\u4e00\u4e2a\u540d\u4e3a SpeedUp\uff08\uff09 \u7684\u65b9\u6cd5\u548c\u53e6\u4e00\u4e2a\u540d\u4e3a PrintState\uff08\uff09 \u7684\u65b9\u6cd5\u3002\u6309\u5982\u4e0b\u65b9\u5f0f\u66f4\u65b0\u60a8\u7684\u8bfe\u7a0b<\/p>\n<p>class Car<br \/>\n{<br \/>\n\/\/ The 'state' of the Car. public string petName; public int currSpeed;<\/p>\n<p>\/\/ The functionality of the Car.<br \/>\n\/\/ Using the expression-bodied member syntax<br \/>\n\/\/ covered in Chapter 4 public void PrintState()<br \/>\n=&gt; Console.WriteLine(&quot;{0} is going {1} MPH.&quot;, petName, currSpeed);<br \/>\npublic void SpeedUp(int delta)<br \/>\n=&gt; currSpeed += delta;<br \/>\n}<br \/>\nPrintState() is more or less a diagnostic function that will simply dump the current state of a given<br \/>\nCar object to the command window. SpeedUp() will increase the speed of the Car object by the amount specified by the incoming int parameter. Now, update your top-level statements in the Program.cs file with the following code:<br \/>\nPrintState\uff08\uff09 \u6216\u591a\u6216\u5c11\u662f\u4e00\u4e2a\u8bca\u65ad\u51fd\u6570\uff0c\u5b83\u5c06\u7b80\u5355\u5730\u8f6c\u50a8\u7ed9\u5b9a\u7684\u5f53\u524d\u72b6\u6001\u547d\u4ee4\u7a97\u53e3\u7684\u6c7d\u8f66\u5bf9\u8c61\u3002 SpeedUp\uff08\uff09 \u5c06\u6309\u4f20\u5165\u7684 int \u53c2\u6570\u6307\u5b9a\u7684\u91cf\u63d0\u9ad8 Car \u5bf9\u8c61\u7684\u901f\u5ea6\u3002\u73b0\u5728\uff0c\u4f7f\u7528\u4ee5\u4e0b\u4ee3\u7801\u66f4\u65b0 Program.cs \u6587\u4ef6\u4e2d\u7684\u9876\u7ea7\u8bed\u53e5\uff1a<\/p>\n<p>using SimplClassExample;<br \/>\nConsole.WriteLine(&quot;<strong><strong><em> Fun with Class Types <\/em><\/strong><\/strong>\\n&quot;);<br \/>\n\/\/ Allocate and configure a Car object. Car myCar = new Car();<br \/>\nmyCar.petName = &quot;Henry&quot;; myCar.currSpeed = 10;<br \/>\n\/\/ Speed up the car a few times and print out the<br \/>\n\/\/ new state.<br \/>\nfor (int i = 0; i &lt;= 10; i++)<br \/>\n{<br \/>\nmyCar.SpeedUp(5); myCar.PrintState();<br \/>\n}<br \/>\nConsole.ReadLine();<\/p>\n<p>After you run your program, you will see that the Car variable (myCar) maintains its current state throughout the life of the application, as shown in the following output:<br \/>\n\u8fd0\u884c\u7a0b\u5e8f\u540e\uff0c\u60a8\u5c06\u770b\u5230 Car \u53d8\u91cf \uff08myCar\uff09 \u5728\u5e94\u7528\u7a0b\u5e8f\u7684\u6574\u4e2a\u751f\u547d\u5468\u671f\u4e2d\u4fdd\u6301\u5176\u5f53\u524d\u72b6\u6001\uff0c\u5982\u4ee5\u4e0b\u8f93\u51fa\u6240\u793a\uff1a<\/p>\n<p><strong><strong><em> Fun with Class Types <\/em><\/strong><\/strong><\/p>\n<p>Henry   is  going   15  MPH.<br \/>\nHenry   is  going   20  MPH.<br \/>\nHenry   is  going   25  MPH.<br \/>\nHenry   is  going   30  MPH.<br \/>\nHenry   is  going   35  MPH.<br \/>\nHenry   is  going   40  MPH.<br \/>\nHenry   is  going   45  MPH.<br \/>\nHenry   is  going   50  MPH.<br \/>\nHenry   is  going   55  MPH.<br \/>\nHenry   is  going   60  MPH.<br \/>\nHenry   is  going   65  MPH.<\/p>\n<h3>Allocating Objects with the new Keyword<\/h3>\n<p>\u4f7f\u7528\u65b0\u5173\u952e\u5b57\u5206\u914d\u5bf9\u8c61<\/p>\n<p>As shown in the previous code example, objects must be allocated into memory using the new keyword. If you do not use the new keyword and attempt to use your class variable in a subsequent code statement, you will receive a compiler error. For example, the following top-level statement will not compile:<br \/>\n\u5982\u524d\u9762\u7684\u4ee3\u7801\u793a\u4f8b\u6240\u793a\uff0c\u5fc5\u987b\u4f7f\u7528 new \u5173\u952e\u5b57\u5c06\u5bf9\u8c61\u5206\u914d\u5230\u5185\u5b58\u4e2d\u3002\u5982\u679c\u4e0d\u4f7f\u7528 new \u5173\u952e\u5b57\u5e76\u5c1d\u8bd5\u5728\u540e\u7eed\u4ee3\u7801\u8bed\u53e5\u4e2d\u4f7f\u7528\u7c7b\u53d8\u91cf\uff0c\u5219\u4f1a\u6536\u5230\u7f16\u8bd1\u5668\u9519\u8bef\u3002\u4f8b\u5982\uff0c\u4ee5\u4e0b\u9876\u7ea7\u8bed\u53e5\u5c06\u65e0\u6cd5\u7f16\u8bd1\uff1a<\/p>\n<p>Console.WriteLine(&quot;<strong><strong><em> Fun with Class Types <\/em><\/strong><\/strong>\\n&quot;);<br \/>\n\/\/ Compiler error! Forgot to use 'new' to create object! Car myCar;<br \/>\nmyCar.petName = &quot;Fred&quot;;<\/p>\n<p>To correctly create an object using the new keyword, you may define and allocate a Car object on a single line of code.<br \/>\n\u8981\u4f7f\u7528 new \u5173\u952e\u5b57\u6b63\u786e\u521b\u5efa\u5bf9\u8c61\uff0c\u53ef\u4ee5\u5728\u4e00\u884c\u4ee3\u7801\u4e0a\u5b9a\u4e49\u548c\u5206\u914d Car \u5bf9\u8c61\u3002<\/p>\n<p>Console.WriteLine(&quot;<strong><strong><em> Fun with Class Types <\/em><\/strong><\/strong>\\n&quot;); Car myCar = new Car();<br \/>\nmyCar.petName = &quot;Fred&quot;;<\/p>\n<p>As an alternative, if you want to define and allocate a class instance on separate lines of code, you may do so as follows:<br \/>\n\u8981\u4f7f\u7528 new \u5173\u952e\u5b57\u6b63\u786e\u521b\u5efa\u5bf9\u8c61\uff0c\u53ef\u4ee5\u5728\u4e00\u884c\u4ee3\u7801\u4e0a\u5b9a\u4e49\u548c\u5206\u914d Car \u5bf9\u8c61\u3002<\/p>\n<p>Console.WriteLine(&quot;<strong><strong><em> Fun with Class Types <\/em><\/strong><\/strong>\\n&quot;); Car myCar;<br \/>\nmyCar = new Car(); myCar.petName = &quot;Fred&quot;;<\/p>\n<p>Here, the first code statement simply declares a reference to a yet-to-be-determined Car object. It is not until you assign a reference to an object that this reference points to a valid object in memory.<br \/>\n\u5728\u8fd9\u91cc\uff0c\u7b2c\u4e00\u4e2a\u4ee3\u7801\u8bed\u53e5\u53ea\u662f\u58f0\u660e\u5bf9\u5c1a\u672a\u786e\u5b9a\u7684 Car \u5bf9\u8c61\u7684\u5f15\u7528\u3002\u76f4\u5230\u60a8\u6307\u5b9a\u5bf9\u5bf9\u8c61\u7684\u5f15\u7528\uff0c\u6b64\u5f15\u7528\u624d\u4f1a\u6307\u5411\u5185\u5b58\u4e2d\u7684\u6709\u6548\u5bf9\u8c61\u3002<br \/>\nIn any case, at this point you have a trivial class that defines a few points of data and some basic operations. To enhance the functionality of the current Car class, you need to understand the role of constructors.<br \/>\n\u65e0\u8bba\u5982\u4f55\uff0c\u6b64\u65f6\u60a8\u6709\u4e00\u4e2a\u7b80\u5355\u7684\u7c7b\uff0c\u5b83\u5b9a\u4e49\u4e86\u51e0\u4e2a\u6570\u636e\u70b9\u548c\u4e00\u4e9b\u57fa\u672c\u64cd\u4f5c\u3002\u82e5\u8981\u589e\u5f3a\u5f53\u524d Car \u7c7b\u7684\u529f\u80fd\uff0c\u9700\u8981\u4e86\u89e3\u6784\u9020\u51fd\u6570\u7684\u89d2\u8272\u3002<\/p>\n<h2>Understanding Constructors<\/h2>\n<p>\u4e86\u89e3\u6784\u9020\u51fd\u6570<\/p>\n<p>Given that objects have state (represented by the values of an object\u2019s member variables), a programmer will typically want to assign relevant values to the object\u2019s field data before use. Currently, the Car class demands that the petName and currSpeed fields be assigned on a field-by-field basis. For the current example, this is not too problematic, given that you have only two public data points. However, it is not uncommon for a class to have dozens of fields to contend with. Clearly, it would be undesirable to author 20 initialization statements to set 20 points of data!<br \/>\n\u7ed9\u5b9a\u5bf9\u8c61\u5177\u6709\u72b6\u6001\uff08\u7531\u5bf9\u8c61\u7684\u6210\u5458\u53d8\u91cf\u7684\u503c\u8868\u793a\uff09\uff0c\u7a0b\u5e8f\u5458\u901a\u5e38\u5e0c\u671b\u5728\u4f7f\u7528\u4e4b\u524d\u4e3a\u5bf9\u8c61\u7684\u5b57\u6bb5\u6570\u636e\u5206\u914d\u76f8\u5173\u503c\u3002\u76ee\u524d\uff0cCar \u7c7b\u8981\u6c42\u9010\u5b57\u6bb5\u5206\u914d petName \u548c currSpeed \u5b57\u6bb5\u3002\u5bf9\u4e8e\u5f53\u524d\u793a\u4f8b\uff0c\u6b64\u95ee\u9898\u4e0d\u5927\uff0c\u56e0\u4e3a\u60a8\u53ea\u6709\u4e24\u4e2a\u516c\u5171\u6570\u636e\u70b9\u3002\u4f46\u662f\uff0c\u4e00\u4e2a\u7c7b\u8981\u5e94\u5bf9\u6570\u5341\u4e2a\u5b57\u6bb5\u7684\u60c5\u51b5\u5e76\u4e0d\u5c11\u89c1\u3002\u663e\u7136\uff0c\u7f16\u5199 20 \u4e2a\u521d\u59cb\u5316\u8bed\u53e5\u6765\u8bbe\u7f6e 20 \u4e2a\u6570\u636e\u70b9\u662f\u4e0d\u53ef\u53d6\u7684\uff01<\/p>\n<p>Thankfully, C# supports the use of constructors, which allow the state of an object to be established at the time of creation. A constructor is a special method of a class that is called indirectly when creating an object using the new keyword. However, unlike a \u201cnormal\u201d method, constructors never have a return value (not even void) and are always named identically to the class they are constructing.<br \/>\n\u503c\u5f97\u5e86\u5e78\u7684\u662f\uff0cC# \u652f\u6301\u4f7f\u7528\u6784\u9020\u51fd\u6570\uff0c\u8fd9\u4e9b\u6784\u9020\u51fd\u6570\u5141\u8bb8\u5728\u521b\u5efa\u65f6\u5efa\u7acb\u5bf9\u8c61\u7684\u72b6\u6001\u3002\u6784\u9020\u51fd\u6570\u662f\u7c7b\u7684\u7279\u6b8a\u65b9\u6cd5\uff0c\u5728\u4f7f\u7528 new \u5173\u952e\u5b57\u521b\u5efa\u5bf9\u8c61\u65f6\u95f4\u63a5\u8c03\u7528\u8be5\u65b9\u6cd5\u3002\u4f46\u662f\uff0c\u4e0e\u201c\u666e\u901a\u201d\u65b9\u6cd5\u4e0d\u540c\uff0c\u6784\u9020\u51fd\u6570\u6c38\u8fdc\u4e0d\u4f1a\u6709\u8fd4\u56de\u503c\uff08\u751a\u81f3\u6ca1\u6709 void\uff09\uff0c\u5e76\u4e14\u59cb\u7ec8\u4e0e\u5b83\u4eec\u6b63\u5728\u6784\u9020\u7684\u7c7b\u76f8\u540c\u3002<\/p>\n<h2>Understanding the Role of the Default Constructor<\/h2>\n<p>\u4e86\u89e3\u9ed8\u8ba4\u6784\u9020\u51fd\u6570\u7684\u89d2\u8272<\/p>\n<p>Every C# class is provided with a \u201cfreebie\u201d default constructor that you can redefine if need be. By definition, a default constructor never takes arguments. After allocating the new object into memory, the default constructor ensures that all field data of the class is set to an appropriate default value (see Chapter 3 for information regarding the default values of C# data types).<br \/>\n\u6bcf\u4e2a C# \u7c7b\u90fd\u63d0\u4f9b\u4e86\u4e00\u4e2a\u201c\u514d\u8d39\u8d60\u54c1\u201d\u9ed8\u8ba4\u6784\u9020\u51fd\u6570\uff0c\u5982\u679c\u9700\u8981\uff0c\u53ef\u4ee5\u91cd\u65b0\u5b9a\u4e49\u8be5\u6784\u9020\u51fd\u6570\u3002\u6839\u636e\u5b9a\u4e49\uff0c\u9ed8\u8ba4\u6784\u9020\u51fd\u6570\u4ece\u4e0d\u63a5\u53d7\u53c2\u6570\u3002\u5c06\u65b0\u5bf9\u8c61\u5206\u914d\u5230\u5185\u5b58\u540e\uff0c\u9ed8\u8ba4\u6784\u9020\u51fd\u6570\u786e\u4fdd\u7c7b\u7684\u6240\u6709\u5b57\u6bb5\u6570\u636e\u90fd\u8bbe\u7f6e\u4e3a\u9002\u5f53\u7684\u9ed8\u8ba4\u503c\uff08\u6709\u5173 C# \u6570\u636e\u7c7b\u578b\u7684\u9ed8\u8ba4\u503c\u7684\u4fe1\u606f\uff0c\u8bf7\u53c2\u9605\u7b2c 3 \u7ae0\uff09\u3002<\/p>\n<p>If you are not satisfied with these default assignments, you may redefine the default constructor to suit your needs. To illustrate, update your C# Car class as follows:<br \/>\n\u5982\u679c\u5bf9\u8fd9\u4e9b\u9ed8\u8ba4\u8d4b\u503c\u4e0d\u6ee1\u610f\uff0c\u53ef\u4ee5\u91cd\u65b0\u5b9a\u4e49\u9ed8\u8ba4\u6784\u9020\u51fd\u6570\u4ee5\u6ee1\u8db3\u60a8\u7684\u9700\u8981\u3002\u4e3a\u4e86\u8fdb\u884c\u8bf4\u660e\uff0c\u8bf7\u66f4\u65b0 C# Car \u7c7b\uff0c\u5982\u4e0b\u6240\u793a\uff1a<\/p>\n<p>class Car<br \/>\n{<br \/>\n\/\/ The 'state' of the Car. public string petName; public int currSpeed;<\/p>\n<p>\/\/ A custom default constructor. public Car()<br \/>\n{<\/p>\n<p>}<br \/>\n...<br \/>\n}<\/p>\n<p>petName = &quot;Chuck&quot;; currSpeed = 10;<\/p>\n<p>In this case, you are forcing all Car objects to begin life named Chuck at a rate of 10 MPH. With this, you are able to create a Car object set to these default values as follows:<br \/>\n\u5728\u672c\u4f8b\u4e2d\uff0c\u60a8\u5c06\u5f3a\u5236\u6240\u6709 Car \u5bf9\u8c61\u4ee5 10 \u82f1\u91cc\/\u5c0f\u65f6\u7684\u901f\u5ea6\u5f00\u59cb\u540d\u4e3a Chuck \u7684\u751f\u547d\u3002\u8fd9\u6837\uff0c\u60a8\u5c31\u53ef\u4ee5\u521b\u5efa\u4e00\u4e2a\u8bbe\u7f6e\u4e3a\u8fd9\u4e9b\u9ed8\u8ba4\u503c\u7684 Car \u5bf9\u8c61\uff0c\u5982\u4e0b\u6240\u793a\uff1a<\/p>\n<p>Console.WriteLine(&quot;<strong><strong><em> Fun with Class Types <\/em><\/strong><\/strong>\\n&quot;);<\/p>\n<p>\/\/ Invoking the default constructor. Car chuck = new Car();<\/p>\n<p>\/\/ Prints &quot;Chuck is going 10 MPH.&quot; chuck.PrintState();<br \/>\n...<\/p>\n<h2>Defining Custom Constructors<\/h2>\n<p>\u5b9a\u4e49\u81ea\u5b9a\u4e49\u6784\u9020\u51fd\u6570<\/p>\n<p>Typically, classes define additional constructors beyond the default. In doing so, you provide the object user with a simple and consistent way to initialize the state of an object directly at the time of creation. Ponder the following update to the Car class, which now supports a total of three constructors:<br \/>\n\u901a\u5e38\uff0c\u7c7b\u5b9a\u4e49\u9ed8\u8ba4\u503c\u4e4b\u5916\u7684\u5176\u4ed6\u6784\u9020\u51fd\u6570\u3002\u8fd9\u6837\uff0c\u60a8\u5c31\u53ef\u4ee5\u4e3a\u5bf9\u8c61\u7528\u6237\u63d0\u4f9b\u4e00\u79cd\u7b80\u5355\u4e14\u4e00\u81f4\u7684\u65b9\u5f0f\u6765\u5728\u521b\u5efa\u65f6\u76f4\u63a5\u521d\u59cb\u5316\u5bf9\u8c61\u7684\u72b6\u6001\u3002\u8003\u8651\u5bf9 Car \u7c7b\u7684\u4ee5\u4e0b\u66f4\u65b0\uff0c\u8be5\u7c7b\u73b0\u5728\u603b\u5171\u652f\u6301\u4e09\u4e2a\u6784\u9020\u51fd\u6570\uff1a<\/p>\n<p>class Car<br \/>\n{<br \/>\n\/\/ The 'state' of the Car. public string petName; public int currSpeed;<\/p>\n<p>\/\/ A custom default constructor. public Car()<br \/>\n{<br \/>\npetName = &quot;Chuck&quot;; currSpeed = 10;<br \/>\n}<\/p>\n<p>\/\/ Here, currSpeed will receive the<br \/>\n\/\/ default value of an int (zero). public Car(string pn)<br \/>\n{<br \/>\npetName = pn;<br \/>\n}<\/p>\n<p>\/\/ Let caller set the full state of the Car. public Car(string pn, int cs)<br \/>\n{<\/p>\n<p>}<br \/>\n...<br \/>\n}<\/p>\n<p>petName = pn; currSpeed = cs;<\/p>\n<p>Keep in mind that what makes one constructor different from another (in the eyes of the C# compiler) is the number of and\/or type of constructor arguments. Recall from Chapter 4, when you define a method of the same name that differs by the number or type of arguments, you have overloaded the method. Thus, the Car class has overloaded the constructor to provide a number of ways to create an object at the time of declaration. In any case, you are now able to create Car objects using any of the public constructors. Here is an example:<br \/>\n\u8bf7\u8bb0\u4f4f\uff0c\u4f7f\u4e00\u4e2a\u6784\u9020\u51fd\u6570\u4e0e\u53e6\u4e00\u4e2a\u6784\u9020\u51fd\u6570\u4e0d\u540c\u7684\uff08\u5728 C# \u7f16\u8bd1\u5668\u773c\u4e2d\uff09\u662f\u6784\u9020\u51fd\u6570\u53c2\u6570\u7684\u6570\u91cf\u548c\/\u6216\u7c7b\u578b\u3002\u56de\u60f3\u4e00\u4e0b\u7b2c 4 \u7ae0\uff0c\u5f53\u60a8\u5b9a\u4e49\u4e00\u4e2a\u56e0\u53c2\u6570\u7684\u6570\u91cf\u6216\u7c7b\u578b\u800c\u5f02\u7684\u540c\u540d\u65b9\u6cd5\u65f6\uff0c\u60a8\u5df2\u7ecf\u91cd\u8f7d\u4e86\u8be5\u65b9\u6cd5\u3002\u56e0\u6b64\uff0cCar \u7c7b\u91cd\u8f7d\u4e86\u6784\u9020\u51fd\u6570\uff0c\u4ee5\u63d0\u4f9b\u591a\u79cd\u5728\u58f0\u660e\u65f6\u521b\u5efa\u5bf9\u8c61\u7684\u65b9\u6cd5\u3002\u5728\u4efb\u4f55\u60c5\u51b5\u4e0b\uff0c\u60a8\u73b0\u5728\u90fd\u53ef\u4ee5\u4f7f\u7528\u4efb\u4f55\u516c\u5171\u6784\u9020\u51fd\u6570\u521b\u5efa Car \u5bf9\u8c61\u3002\u4e0b\u9762\u662f\u4e00\u4e2a\u793a\u4f8b\uff1a<\/p>\n<p>Console.WriteLine(&quot;<strong><strong><em> Fun with Class Types <\/em><\/strong><\/strong>\\n&quot;);<\/p>\n<p>\/\/ Make a Car called Chuck going 10 MPH. Car chuck = new Car(); chuck.PrintState();<\/p>\n<p>\/\/ Make a Car called Mary going 0 MPH. Car mary = new Car(&quot;Mary&quot;); mary.PrintState();<\/p>\n<p>\/\/ Make a Car called Daisy going 75 MPH. Car daisy = new Car(&quot;Daisy&quot;, 75); daisy.PrintState();<br \/>\n...<\/p>\n<h3>Constructors As Expression-Bodied Members (New 7.0)<\/h3>\n<p>\u6784\u9020\u51fd\u6570\u4f5c\u4e3a\u8868\u8fbe\u5f0f\u4f53\u6210\u5458 \uff08New 7.0\uff09<\/p>\n<p>C# 7 added additional uses for the expression-bodied member style. Constructors, finalizers, and get\/set accessors on properties and indexers now accept the new syntax. With this in mind, the previous constructors can be written like this:<br \/>\nC# 7 \u4e3a\u8868\u8fbe\u5f0f\u4e3b\u4f53\u6210\u5458\u6837\u5f0f\u6dfb\u52a0\u4e86\u5176\u4ed6\u7528\u6cd5\u3002\u5c5e\u6027\u548c\u7d22\u5f15\u5668\u4e0a\u7684\u6784\u9020\u51fd\u6570\u3001\u7ec8\u7ed3\u5668\u548c get\/set \u8bbf\u95ee\u5668\u73b0\u5728\u63a5\u53d7\u65b0\u8bed\u6cd5\u3002\u8003\u8651\u5230\u8fd9\u4e00\u70b9\uff0c\u524d\u9762\u7684\u6784\u9020\u51fd\u6570\u53ef\u4ee5\u8fd9\u6837\u7f16\u5199\uff1a<\/p>\n<p>\/\/ Here, currSpeed will receive the<br \/>\n\/\/ default value of an int (zero). public Car(string pn) =&gt; petName = pn;<\/p>\n<p>The second custom constructor cannot be converted to an expression since expression-bodied members must be one-line methods.<br \/>\n\u7b2c\u4e8c\u4e2a\u81ea\u5b9a\u4e49\u6784\u9020\u51fd\u6570\u4e0d\u80fd\u8f6c\u6362\u4e3a\u8868\u8fbe\u5f0f\uff0c\u56e0\u4e3a\u8868\u8fbe\u5f0f\u4f53\u6210\u5458\u5fc5\u987b\u662f\u5355\u884c\u65b9\u6cd5\u3002<\/p>\n<h2>Constructors with out Parameters (New 7.3)<\/h2>\n<p>\u4e0d\u5e26 out \u53c2\u6570\u7684\u6784\u9020\u51fd\u6570\uff08\u65b0 7.3\uff09<\/p>\n<p>Constructors (as well as field and property initializers, covered later) can use out parameters starting with C# 7.3. For a trivial example of this, add the following constructor to the Car class:<br \/>\n\u6784\u9020\u51fd\u6570\uff08\u4ee5\u53ca\u5b57\u6bb5\u548c\u5c5e\u6027\u521d\u59cb\u503c\u8bbe\u5b9a\u9879\uff0c\u7a0d\u540e\u5c06\u4ecb\u7ecd\uff09\u53ef\u4ee5\u4f7f\u7528\u4ece C# 7.3 \u5f00\u59cb\u7684\u53c2\u6570\u3002\u5bf9\u4e8e\u8fd9\u65b9\u9762\u7684\u7b80\u5355\u793a\u4f8b\uff0c\u8bf7\u5c06\u4ee5\u4e0b\u6784\u9020\u51fd\u6570\u6dfb\u52a0\u5230 Car \u7c7b\uff1a<\/p>\n<p>public Car(string pn, int cs, out bool inDanger)<br \/>\n{<br \/>\npetName = pn; currSpeed = cs; if (cs &gt; 100)<br \/>\n{<br \/>\ninDanger = true;<br \/>\n}<br \/>\nelse<br \/>\n{<br \/>\ninDanger = false;<br \/>\n}<br \/>\n}<\/p>\n<p>All of the rules of out parameters must be followed. In this example, the inDanger parameter must be assigned a value before the conclusion of the constructor.<br \/>\n\u5fc5\u987b\u9075\u5b88 out \u53c2\u6570\u7684\u6240\u6709\u89c4\u5219\u3002\u5728\u6b64\u793a\u4f8b\u4e2d\uff0c\u5fc5\u987b\u5728\u6784\u9020\u51fd\u6570\u7ed3\u675f\u4e4b\u524d\u4e3a inDanger \u53c2\u6570\u8d4b\u503c\u3002<\/p>\n<h2>Understanding the Default Constructor Revisited<\/h2>\n<p>\u91cd\u65b0\u8bbf\u95ee\u9ed8\u8ba4\u6784\u9020\u51fd\u6570<br \/>\nAs you have just learned, all classes are provided with a free default constructor. Insert a new file into your project named Motorcycle.cs, and add the following to define a Motorcycle class:<br \/>\n\u6b63\u5982\u60a8\u521a\u521a\u4e86\u89e3\u5230\u7684\uff0c\u6240\u6709\u7c7b\u90fd\u63d0\u4f9b\u4e86\u4e00\u4e2a\u514d\u8d39\u7684\u9ed8\u8ba4\u6784\u9020\u51fd\u6570\u3002\u5c06\u4e00\u4e2a\u540d\u4e3a Motorcycle.cs \u7684\u65b0\u6587\u4ef6\u63d2\u5165\u5230\u9879\u76ee\u4e2d\uff0c\u5e76\u6dfb\u52a0\u4ee5\u4e0b\u5185\u5bb9\u4ee5\u5b9a\u4e49 Motorcycle \u7c7b\uff1a<\/p>\n<p>namespace SimpleClassExample; class Motorcycle<br \/>\n{<br \/>\npublic void PopAWheely()<\/p>\n<p>{<br \/>\nConsole.WriteLine(&quot;Yeeeeeee Haaaaaeewww!&quot;);<br \/>\n}<br \/>\n}<\/p>\n<p>Now you are able to create an instance of the Motorcycle type via the default constructor out of the box.<br \/>\n\u73b0\u5728\uff0c\u60a8\u53ef\u4ee5\u901a\u8fc7\u73b0\u6210\u7684\u9ed8\u8ba4\u6784\u9020\u51fd\u6570\u521b\u5efa Motorcycle \u7c7b\u578b\u7684\u5b9e\u4f8b\u3002<\/p>\n<p>Console.WriteLine(&quot;<strong><strong><em> Fun with Class Types <\/em><\/strong><\/strong>\\n&quot;); Motorcycle mc = new Motorcycle();<br \/>\nmc.PopAWheely();<br \/>\n...<\/p>\n<p>However, as soon as you define a custom constructor with any number of parameters, the default constructor is silently removed from the class and is no longer available. Think of it this way: if you do not define a custom constructor, the C# compiler grants you a default to allow the object user to allocate an instance of your type with the field data set to the correct default values. However, when you define a unique constructor, the compiler assumes you have taken matters into your own hands.<br \/>\n\u4f46\u662f\uff0c\u4e00\u65e6\u5b9a\u4e49\u4e86\u5177\u6709\u4efb\u610f\u6570\u91cf\u53c2\u6570\u7684\u81ea\u5b9a\u4e49\u6784\u9020\u51fd\u6570\uff0c\u9ed8\u8ba4\u6784\u9020\u51fd\u6570\u5c31\u4f1a\u4ece\u7c7b\u4e2d\u4ee5\u9759\u9ed8\u65b9\u5f0f\u5220\u9664\uff0c\u5e76\u4e14\u4e0d\u518d\u53ef\u7528\u3002\u53ef\u4ee5\u8fd9\u6837\u60f3\uff1a\u5982\u679c\u672a\u5b9a\u4e49\u81ea\u5b9a\u4e49\u6784\u9020\u51fd\u6570\uff0cC# \u7f16\u8bd1\u5668\u5c06\u6388\u4e88\u9ed8\u8ba4\u503c\uff0c\u4ee5\u5141\u8bb8\u5bf9\u8c61\u7528\u6237\u5c06\u5b57\u6bb5\u6570\u636e\u96c6\u5206\u914d\u7ed9\u6b63\u786e\u7684\u9ed8\u8ba4\u503c\u7684\u7c7b\u578b\u7684\u5b9e\u4f8b\u3002\u4f46\u662f\uff0c\u5f53\u60a8\u5b9a\u4e49\u552f\u4e00\u7684\u6784\u9020\u51fd\u6570\u65f6\uff0c\u7f16\u8bd1\u5668\u4f1a\u5047\u5b9a\u60a8\u5df2\u5c06\u4e8b\u60c5\u638c\u63e1\u5728\u81ea\u5df1\u624b\u4e2d\u3002<\/p>\n<p>Therefore, if you want to allow the object user to create an instance of your type with the default constructor, as well as your custom constructor, you must explicitly redefine the default. To this end, understand that in a vast majority of cases, the implementation of the default constructor of a class is intentionally empty, as all you require is the ability to create an object with default values. Consider the following update to the Motorcycle class:<br \/>\n\u56e0\u6b64\uff0c\u5982\u679c\u8981\u5141\u8bb8\u5bf9\u8c61\u7528\u6237\u4f7f\u7528\u9ed8\u8ba4\u6784\u9020\u51fd\u6570\u4ee5\u53ca\u81ea\u5b9a\u4e49\u6784\u9020\u51fd\u6570\u521b\u5efa\u7c7b\u578b\u7684\u5b9e\u4f8b\uff0c\u5219\u5fc5\u987b\u663e\u5f0f\u91cd\u65b0\u5b9a\u4e49\u9ed8\u8ba4\u503c\u3002\u4e3a\u6b64\uff0c\u8bf7\u4e86\u89e3\uff0c\u5728\u7edd\u5927\u591a\u6570\u60c5\u51b5\u4e0b\uff0c\u7c7b\u7684\u9ed8\u8ba4\u6784\u9020\u51fd\u6570\u7684\u5b9e\u73b0\u662f\u6545\u610f\u4e3a\u7a7a\u7684\uff0c\u56e0\u4e3a\u60a8\u6240\u9700\u8981\u7684\u53ea\u662f\u80fd\u591f\u4f7f\u7528\u9ed8\u8ba4\u503c\u521b\u5efa\u5bf9\u8c61\u3002\u8bf7\u8003\u8651\u5bf9\u6469\u6258\u8f66\u7c7b\u8fdb\u884c\u4ee5\u4e0b\u66f4\u65b0\uff1a<\/p>\n<p>class Motorcycle<br \/>\n{<br \/>\npublic int driverIntensity;<\/p>\n<p>public void PopAWheely()<br \/>\n{<br \/>\nfor (int i = 0; i &lt;= driverIntensity; i++)<br \/>\n{<br \/>\nConsole.WriteLine(&quot;Yeeeeeee Haaaaaeewww!&quot;);<br \/>\n}<br \/>\n}<br \/>\n\/\/ Put back the default constructor, which will<br \/>\n\/\/ set all data members to default values. public Motorcycle() {}<\/p>\n<p>\/\/ Our custom constructor. public Motorcycle(int intensity)<br \/>\n{<br \/>\ndriverIntensity = intensity;<br \/>\n}<br \/>\n}<\/p>\n<p>\u25a0 Note now that you better understand the role of class constructors, here is a nice shortcut. Both Visual studio and Visual studio Code provide the ctor code snippet. When you type ctor and press the Tab key, the ide will automatically define a custom default constructor. You can then add custom parameters and implementation logic. give it a try.<br \/>\n\u56e0\u6b64\uff0c\u5982\u679c\u8981\u5141\u8bb8\u5bf9\u8c61\u7528\u6237\u4f7f\u7528\u9ed8\u8ba4\u6784\u9020\u51fd\u6570\u4ee5\u53ca\u81ea\u5b9a\u4e49\u6784\u9020\u51fd\u6570\u521b\u5efa\u7c7b\u578b\u7684\u5b9e\u4f8b\uff0c\u5219\u5fc5\u987b\u663e\u5f0f\u91cd\u65b0\u5b9a\u4e49\u9ed8\u8ba4\u503c\u3002\u4e3a\u6b64\uff0c\u8bf7\u4e86\u89e3\uff0c\u5728\u7edd\u5927\u591a\u6570\u60c5\u51b5\u4e0b\uff0c\u7c7b\u7684\u9ed8\u8ba4\u6784\u9020\u51fd\u6570\u7684\u5b9e\u73b0\u662f\u6545\u610f\u4e3a\u7a7a\u7684\uff0c\u56e0\u4e3a\u60a8\u6240\u9700\u8981\u7684\u53ea\u662f\u80fd\u591f\u4f7f\u7528\u9ed8\u8ba4\u503c\u521b\u5efa\u5bf9\u8c61\u3002\u8bf7\u8003\u8651\u5bf9\u6469\u6258\u8f66\u7c7b\u8fdb\u884c\u4ee5\u4e0b\u66f4\u65b0\uff1a<\/p>\n<h2>Understanding the Role of the this Keyword<\/h2>\n<p>\u4e86\u89e3\u6b64\u5173\u952e\u5b57\u7684\u4f5c\u7528<\/p>\n<p>C# supplies a this keyword that provides access to the current class instance. One possible use of the this keyword is to resolve scope ambiguity, which can arise when an incoming parameter is named identically to a data field of the class. However, you could simply adopt a naming convention that does not result in such ambiguity; to illustrate this use of the this keyword, update your Motorcycle class with a new string field (named name) to represent the driver\u2019s name. Next, add a method named SetDriverName() implemented as follows:<br \/>\nC# \u63d0\u4f9b\u4e86\u4e00\u4e2a this \u5173\u952e\u5b57\uff0c\u8be5\u5173\u952e\u5b57\u63d0\u4f9b\u5bf9\u5f53\u524d\u7c7b\u5b9e\u4f8b\u7684\u8bbf\u95ee\u3002this \u5173\u952e\u5b57\u7684\u4e00\u79cd\u53ef\u80fd\u7528\u9014\u662f\u89e3\u51b3\u8303\u56f4\u6b67\u4e49\uff0c\u5f53\u4f20\u5165\u53c2\u6570\u4e0e\u7c7b\u7684\u6570\u636e\u5b57\u6bb5\u547d\u540d\u76f8\u540c\u65f6\uff0c\u53ef\u80fd\u4f1a\u51fa\u73b0\u6b67\u4e49\u3002\u4f46\u662f\uff0c\u60a8\u53ef\u4ee5\u7b80\u5355\u5730\u91c7\u7528\u4e0d\u4f1a\u5bfc\u81f4\u8fd9\u79cd\u6b67\u4e49\u7684\u547d\u540d\u7ea6\u5b9a;\u82e5\u8981\u8bf4\u660e this \u5173\u952e\u5b57\u7684\u8fd9\u79cd\u7528\u6cd5\uff0c\u8bf7\u4f7f\u7528\u65b0\u7684\u5b57\u7b26\u4e32\u5b57\u6bb5\uff08\u540d\u4e3a\u540d\u79f0\uff09\u66f4\u65b0 Motorcycle \u7c7b\u4ee5\u8868\u793a\u9a71\u52a8\u7a0b\u5e8f\u7684\u540d\u79f0\u3002\u63a5\u4e0b\u6765\uff0c\u6dfb\u52a0\u4e00\u4e2a\u540d\u4e3a SetDriverName\uff08\uff09 \u7684\u65b9\u6cd5\uff0c\u5b9e\u73b0\u5982\u4e0b\uff1a<\/p>\n<p>class Motorcycle<br \/>\n{<br \/>\npublic int driverIntensity;<\/p>\n<p>\/\/ New members to represent the name of the driver. public string name;<br \/>\npublic void SetDriverName(string name) =&gt; name = name;<br \/>\n...<br \/>\n}<\/p>\n<p>Although this code will compile, the C# compiler will display a warning message informing you that you have assigned a variable back to itself! To illustrate, update your code to call SetDriverName() and then print out the value of the name field. You might be surprised to find that the value of the name field is an empty string!<br \/>\n\u5c3d\u7ba1\u6b64\u4ee3\u7801\u5c06\u7f16\u8bd1\uff0c\u4f46 C# \u7f16\u8bd1\u5668\u5c06\u663e\u793a\u4e00\u6761\u8b66\u544a\u6d88\u606f\uff0c\u901a\u77e5\u60a8\u5df2\u5c06\u53d8\u91cf\u8d4b\u56de\u81ea\u8eab\uff01\u4e3a\u4e86\u8bf4\u660e\u8fd9\u4e00\u70b9\uff0c\u8bf7\u66f4\u65b0\u4ee3\u7801\u4ee5\u8c03\u7528 SetDriverName\uff08\uff09\uff0c\u7136\u540e\u6253\u5370\u51fa\u540d\u79f0\u5b57\u6bb5\u7684\u503c\u3002\u60a8\u53ef\u80fd\u4f1a\u60ca\u8bb6\u5730\u53d1\u73b0\u540d\u79f0\u5b57\u6bb5\u7684\u503c\u662f\u4e00\u4e2a\u7a7a\u5b57\u7b26\u4e32\uff01<\/p>\n<p>\/\/ Make a Motorcycle with a rider named Tiny? Motorcycle c = new Motorcycle(5); c.SetDriverName(&quot;Tiny&quot;);<br \/>\nc.PopAWheely();<br \/>\nConsole.WriteLine(&quot;Rider name is {0}&quot;, c.name); \/\/ Prints an empty name value!<\/p>\n<p>The problem is that the implementation of SetDriverName() is assigning the incoming parameter back to itself given that the compiler assumes name is referring to the variable currently in the method scope rather than the name field at the class scope. To inform the compiler that you want to set the current object\u2019s name data field to the incoming name parameter, simply use this to resolve the ambiguity.<br \/>\n\u95ee\u9898\u5728\u4e8e\uff0cSetDriverName\uff08\uff09 \u7684\u5b9e\u73b0\u5c06\u4f20\u5165\u53c2\u6570\u8d4b\u56de\u81ea\u8eab\uff0c\u56e0\u4e3a\u7f16\u8bd1\u5668\u5047\u5b9a name \u5f15\u7528\u5f53\u524d\u5728\u65b9\u6cd5\u4f5c\u7528\u57df\u4e2d\u7684\u53d8\u91cf\uff0c\u800c\u4e0d\u662f\u7c7b\u4f5c\u7528\u57df\u4e2d\u7684 name \u5b57\u6bb5\u3002\u8981\u901a\u77e5\u7f16\u8bd1\u5668\u8981\u5c06\u5f53\u524d\u5bf9\u8c61\u7684\u540d\u79f0\u6570\u636e\u5b57\u6bb5\u8bbe\u7f6e\u4e3a\u4f20\u5165\u7684 name \u53c2\u6570\uff0c\u53ea\u9700\u4f7f\u7528\u5b83\u6765\u89e3\u51b3\u6b67\u4e49\u3002<\/p>\n<p>public void SetDriverName(string name) =&gt; this.name = name;<\/p>\n<p>If there is no ambiguity, you are not required to make use of the this keyword when accessing data fields or members. For example, if you rename the string data member from name to driverName (which will also require you to update your top-level statements), the use of this is optional as there is no longer a scope ambiguity.<br \/>\n\u5982\u679c\u6ca1\u6709\u6b67\u4e49\uff0c\u5219\u5728\u8bbf\u95ee\u6570\u636e\u5b57\u6bb5\u6216\u6210\u5458\u65f6\u4e0d\u9700\u8981\u4f7f\u7528 this \u5173\u952e\u5b57\u3002\u4f8b\u5982\uff0c\u5982\u679c\u5c06\u5b57\u7b26\u4e32\u6570\u636e\u6210\u5458\u4ece name \u91cd\u547d\u540d\u4e3a driverName\uff08\u8fd9\u4e5f\u9700\u8981\u66f4\u65b0\u9876\u7ea7\u8bed\u53e5\uff09\uff0c\u5219 this \u7684\u4f7f\u7528\u662f\u53ef\u9009\u7684\uff0c\u56e0\u4e3a\u4e0d\u518d\u5b58\u5728\u8303\u56f4\u6b67\u4e49\u3002<\/p>\n<p>class Motorcycle<br \/>\n{<br \/>\npublic int driverIntensity; public string driverName;<\/p>\n<p>public void SetDriverName(string name)<br \/>\n{<br \/>\n\/\/ These two statements are functionally the same. driverName = name;<\/p>\n<p>this.driverName = name;<br \/>\n}<br \/>\n...<br \/>\n}<\/p>\n<p>Even though there is little to be gained when using this in unambiguous situations, you might still find this keyword useful when implementing class members, as IDEs such as Visual Studio and Visual Studio Code will enable IntelliSense when this is specified. This can be helpful when you have forgotten the name of a class member and want to quickly recall the definition.<br \/>\n\u5c3d\u7ba1\u5728\u660e\u786e\u7684\u60c5\u51b5\u4e0b\u4f7f\u7528\u5b83\u65f6\u51e0\u4e4e\u6ca1\u6709\u4ec0\u4e48\u597d\u5904\uff0c\u4f46\u5728\u5b9e\u73b0\u7c7b\u6210\u5458\u65f6\uff0c\u60a8\u4ecd\u7136\u53ef\u80fd\u4f1a\u53d1\u73b0\u6b64\u5173\u952e\u5b57\u5f88\u6709\u7528\uff0c\u56e0\u4e3a IDE \uff08\u5982 Visual Studio \u548c Visual Studio Code\uff09\u5c06\u5728\u6307\u5b9a\u6b64\u5173\u952e\u5b57\u65f6\u542f\u7528 IntelliSense\u3002\u5f53\u60a8\u5fd8\u8bb0\u4e86\u7c7b\u6210\u5458\u7684\u540d\u79f0\u5e76\u5e0c\u671b\u5feb\u901f\u8c03\u7528\u5b9a\u4e49\u65f6\uff0c\u8fd9\u4f1a\u5f88\u6709\u5e2e\u52a9\u3002<\/p>\n<p>\u25a0 Note a common naming convention is to start private (or internal) class-level variable names with an underscore (e.g., _driverName) so intellisense shows all of your variables at the top of the list. in our trivial example, all of the fields are public, so this naming convention would not apply. Through the rest of the book, you will see private and internal variables named with a leading underscore.<br \/>\n\u8bf7\u6ce8\u610f\uff0c\u5e38\u89c1\u7684\u547d\u540d\u7ea6\u5b9a\u662f\u4ee5\u4e0b\u5212\u7ebf\uff08\u4f8b\u5982_driverName\uff09\u5f00\u59cb\u79c1\u6709\uff08\u6216\u5185\u90e8\uff09\u7c7b\u7ea7\u53d8\u91cf\u540d\u79f0\uff0c\u4ee5\u4fbf\u667a\u80fd\u611f\u77e5\u5728\u5217\u8868\u9876\u90e8\u663e\u793a\u6240\u6709\u53d8\u91cf\u3002\u5728\u6211\u4eec\u7684\u7b80\u5355\u793a\u4f8b\u4e2d\uff0c\u6240\u6709\u5b57\u6bb5\u90fd\u662f\u516c\u5171\u7684\uff0c\u56e0\u6b64\u6b64\u547d\u540d\u7ea6\u5b9a\u4e0d\u9002\u7528\u3002\u5728\u672c\u4e66\u7684\u5176\u4f59\u90e8\u5206\uff0c\u60a8\u5c06\u770b\u5230\u4ee5\u524d\u5bfc\u4e0b\u5212\u7ebf\u547d\u540d\u7684\u79c1\u6709\u53d8\u91cf\u548c\u5185\u90e8\u53d8\u91cf\u3002<\/p>\n<h2>Chaining Constructor Calls Using this<\/h2>\n<p>\u4ee5\u6b64\u94fe\u63a5\u6784\u9020\u51fd\u6570\u8c03\u7528<\/p>\n<p>Another use of the this keyword is to design a class using a technique termed constructor chaining. This design pattern is helpful when you have a class that defines multiple constructors. Given that constructors often validate the incoming arguments to enforce various business rules, it can be quite common to find redundant validation logic within a class\u2019s constructor set. Consider the following updated Motorcycle:<br \/>\nthis \u5173\u952e\u5b57\u7684\u53e6\u4e00\u4e2a\u7528\u9014\u662f\u4f7f\u7528\u79f0\u4e3a\u6784\u9020\u51fd\u6570\u94fe\u63a5\u7684\u6280\u672f\u8bbe\u8ba1\u7c7b\u3002\u5f53\u60a8\u6709\u4e00\u4e2a\u5b9a\u4e49\u591a\u4e2a\u6784\u9020\u51fd\u6570\u7684\u7c7b\u65f6\uff0c\u6b64\u8bbe\u8ba1\u6a21\u5f0f\u975e\u5e38\u6709\u7528\u3002\u9274\u4e8e\u6784\u9020\u51fd\u6570\u7ecf\u5e38\u9a8c\u8bc1\u4f20\u5165\u7684\u53c2\u6570\u4ee5\u5f3a\u5236\u5b9e\u65bd\u5404\u79cd\u4e1a\u52a1\u89c4\u5219\uff0c\u56e0\u6b64\u5728\u7c7b\u7684\u6784\u9020\u51fd\u6570\u96c6\u4e2d\u627e\u5230\u5197\u4f59\u9a8c\u8bc1\u903b\u8f91\u662f\u5f88\u5e38\u89c1\u7684\u3002\u8003\u8651\u4ee5\u4e0b\u66f4\u65b0\u7684\u6469\u6258\u8f66\uff1a<\/p>\n<p>class Motorcycle<br \/>\n{<br \/>\npublic int driverIntensity; public string driverName;<\/p>\n<p>public Motorcycle() { }<\/p>\n<p>\/\/ Redundant constructor logic! public Motorcycle(int intensity)<br \/>\n{<br \/>\nif (intensity &gt; 10)<br \/>\n{<br \/>\nintensity = 10;<br \/>\n}<br \/>\ndriverIntensity = intensity;<br \/>\n}<\/p>\n<p>public Motorcycle(int intensity, string name)<br \/>\n{<br \/>\nif (intensity &gt; 10)<br \/>\n{<br \/>\nintensity = 10;<br \/>\n}<\/p>\n<p>}<br \/>\n...<br \/>\n}<\/p>\n<p>driverIntensity = intensity; driverName = name;<\/p>\n<p>Here (perhaps in an attempt to ensure the safety of the rider) each constructor is ensuring that the intensity level is never greater than 10. While this is all well and good, you do have redundant code statements in two constructors. This is less than ideal, as you are now required to update code in multiple locations if your rules change (e.g., if the intensity should not be greater than 5 rather than 10).<br \/>\n\u5728\u8fd9\u91cc\uff08\u4e5f\u8bb8\u662f\u4e3a\u4e86\u786e\u4fdd\u9a91\u624b\u7684\u5b89\u5168\uff09\uff0c\u6bcf\u4e2a\u6784\u9020\u51fd\u6570\u90fd\u786e\u4fdd\u5f3a\u5ea6\u7ea7\u522b\u6c38\u8fdc\u4e0d\u4f1a\u5927\u4e8e 10\u3002\u867d\u7136\u8fd9\u4e00\u5207\u90fd\u5f88\u597d\uff0c\u4f46\u60a8\u786e\u5b9e\u6709\u5197\u4f59\u4ee3\u7801\u4e24\u4e2a\u6784\u9020\u51fd\u6570\u4e2d\u7684\u8bed\u53e5\u3002\u8fd9\u4e0d\u592a\u7406\u60f3\uff0c\u56e0\u4e3a\u5982\u679c\u60a8\u7684\u89c4\u5219\u53d1\u751f\u53d8\u5316\uff08\u4f8b\u5982\uff0c\u5982\u679c\u5f3a\u5ea6\u4e0d\u5e94\u5927\u4e8e 5 \u800c\u4e0d\u662f 10\uff09\uff0c\u60a8\u73b0\u5728\u9700\u8981\u5728\u591a\u4e2a\u4f4d\u7f6e\u66f4\u65b0\u4ee3\u7801\u3002<\/p>\n<p>One way to improve the current situation is to define a method in the Motorcycle class that will validate the incoming argument(s). If you were to do so, each constructor could make a call to this method before making the field assignment(s). While this approach does allow you to isolate the code you need to update when the business rules change, you are now dealing with the following redundancy:<br \/>\n\u6539\u5584\u5f53\u524d\u60c5\u51b5\u7684\u4e00\u79cd\u65b9\u6cd5\u662f\u5728 Motorcycle \u7c7b\u4e2d\u5b9a\u4e49\u4e00\u4e2a\u65b9\u6cd5\uff0c\u8be5\u65b9\u6cd5\u5c06\u9a8c\u8bc1\u4f20\u5165\u7684\u53c2\u6570\u3002\u5982\u679c\u8981\u8fd9\u6837\u505a\uff0c\u5219\u6bcf\u4e2a\u6784\u9020\u51fd\u6570\u90fd\u53ef\u4ee5\u5728\u8fdb\u884c\u5b57\u6bb5\u5206\u914d\u4e4b\u524d\u8c03\u7528\u6b64\u65b9\u6cd5\u3002\u867d\u7136\u6b64\u65b9\u6cd5\u786e\u5b9e\u5141\u8bb8\u60a8\u9694\u79bb\u5728\u4e1a\u52a1\u89c4\u5219\u66f4\u6539\u65f6\u9700\u8981\u66f4\u65b0\u7684\u4ee3\u7801\uff0c\u4f46\u60a8\u73b0\u5728\u6b63\u5728\u5904\u7406\u4ee5\u4e0b\u5197\u4f59\uff1a<\/p>\n<p>class Motorcycle<br \/>\n{<br \/>\npublic int driverIntensity; public string driverName;<\/p>\n<p>\/\/ Constructors.<br \/>\npublic Motorcycle() { }<\/p>\n<p>public Motorcycle(int intensity)<br \/>\n{<br \/>\nSetIntensity(intensity);<br \/>\n}<\/p>\n<p>public Motorcycle(int intensity, string name)<br \/>\n{<br \/>\nSetIntensity(intensity); driverName = name;<br \/>\n}<\/p>\n<p>public void SetIntensity(int intensity)<br \/>\n{<br \/>\nif (intensity &gt; 10)<br \/>\n{<br \/>\nintensity = 10;<br \/>\n}<br \/>\ndriverIntensity = intensity;<br \/>\n}<br \/>\n...<br \/>\n}<\/p>\n<p>A cleaner approach is to designate the constructor that takes the greatest number of arguments as the \u201cmaster constructor\u201d and have its implementation perform the required validation logic. The remaining constructors can make use of the this keyword to forward the incoming arguments to the master constructor and provide any additional parameters as necessary. In this way, you need to worry only about maintaining a single constructor for the entire class, while the remaining constructors are basically empty.<br \/>\n\u66f4\u7b80\u6d01\u7684\u65b9\u6cd5\u662f\u5c06\u63a5\u53d7\u6700\u591a\u53c2\u6570\u7684\u6784\u9020\u51fd\u6570\u6307\u5b9a\u4e3a\u201c\u4e3b\u6784\u9020\u51fd\u6570\u201d\uff0c\u5e76\u8ba9\u5176\u5b9e\u73b0\u6267\u884c\u6240\u9700\u7684\u9a8c\u8bc1\u903b\u8f91\u3002\u5176\u4f59\u6784\u9020\u51fd\u6570\u53ef\u4ee5\u4f7f\u7528 this \u5173\u952e\u5b57\u5c06\u4f20\u5165\u53c2\u6570\u8f6c\u53d1\u7ed9\u4e3b\u6784\u9020\u51fd\u6570\uff0c\u5e76\u6839\u636e\u9700\u8981\u63d0\u4f9b\u4efb\u4f55\u5176\u4ed6\u53c2\u6570\u3002\u8fd9\u6837\uff0c\u60a8\u53ea\u9700\u8981\u62c5\u5fc3\u4e3a\u6574\u4e2a\u7c7b\u7ef4\u62a4\u5355\u4e2a\u6784\u9020\u51fd\u6570\uff0c\u800c\u5176\u4f59\u7684\u6784\u9020\u51fd\u6570\u57fa\u672c\u4e0a\u662f\u7a7a\u7684\u3002<\/p>\n<p>Here is the final iteration of the Motorcycle class (with one additional constructor for the sake of illustration). When chaining constructors, note how the this keyword is \u201cdangling\u201d off the constructor\u2019s declaration (via a colon operator) outside the scope of the constructor itself.<br \/>\n\u4e0b\u9762\u662f Motorcycle \u7c7b\u7684\u6700\u7ec8\u8fed\u4ee3\uff08\u4e3a\u4e86\u4fbf\u4e8e\u8bf4\u660e\uff0c\u8fd8\u6709\u4e00\u4e2a\u989d\u5916\u7684\u6784\u9020\u51fd\u6570\uff09\u3002\u94fe\u63a5\u6784\u9020\u51fd\u6570\u65f6\uff0c\u8bf7\u6ce8\u610f this \u5173\u952e\u5b57\u5982\u4f55\u5728\u6784\u9020\u51fd\u6570\u672c\u8eab\u7684\u8303\u56f4\u4e4b\u5916\u201c\u60ac\u7a7a\u201d\u6784\u9020\u51fd\u6570\u7684\u58f0\u660e\uff08\u901a\u8fc7\u5192\u53f7\u8fd0\u7b97\u7b26\uff09\u3002<\/p>\n<p>class Motorcycle<br \/>\n{<br \/>\npublic int driverIntensity; public string driverName;<\/p>\n<p>\/\/ Constructor chaining. public Motorcycle() {}<br \/>\npublic Motorcycle(int intensity)<br \/>\n: this(intensity, &quot;&quot;) {} public Motorcycle(string name)<br \/>\n: this(0, name) {}<\/p>\n<p>\/\/ This is the 'master' constructor that does all the real work. public Motorcycle(int intensity, string name)<br \/>\n{<br \/>\nif (intensity &gt; 10)<br \/>\n{<br \/>\nintensity = 10;<br \/>\n}<\/p>\n<p>}<br \/>\n...<br \/>\n}<\/p>\n<p>driverIntensity = intensity; driverName = name;<\/p>\n<p>Understand that using the this keyword to chain constructor calls is never mandatory. However, when you make use of this technique, you do tend to end up with a more maintainable and concise class definition. Again, using this technique, you can simplify your programming tasks, as the real work is delegated to a single constructor (typically the constructor that has the most parameters), while the other constructors simply \u201cpass the buck.\u201d<br \/>\n\u4e86\u89e3\u4f7f\u7528 this \u5173\u952e\u5b57\u94fe\u63a5\u6784\u9020\u51fd\u6570\u8c03\u7528\u4ece\u6765\u90fd\u4e0d\u662f\u5f3a\u5236\u6027\u7684\u3002\u4f46\u662f\uff0c\u5f53\u60a8\u4f7f\u7528\u6b64\u6280\u672f\u65f6\uff0c\u60a8\u6700\u7ec8\u5f80\u5f80\u4f1a\u5f97\u5230\u4e00\u4e2a\u66f4\u6613\u4e8e\u7ef4\u62a4\u3001\u66f4\u7b80\u6d01\u7684\u7c7b\u5b9a\u4e49\u3002\u540c\u6837\uff0c\u4f7f\u7528\u6b64\u6280\u672f\uff0c\u60a8\u53ef\u4ee5\u7b80\u5316\u7f16\u7a0b\u4efb\u52a1\uff0c\u56e0\u4e3a\u5b9e\u9645\u5de5\u4f5c\u59d4\u6258\u7ed9\u5355\u4e2a\u6784\u9020\u51fd\u6570\uff08\u901a\u5e38\u662f\u5177\u6709\u6700\u591a\u53c2\u6570\u7684\u6784\u9020\u51fd\u6570\uff09\uff0c\u800c\u5176\u4ed6\u6784\u9020\u51fd\u6570\u53ea\u662f\u201c\u63a8\u5378\u8d23\u4efb\u201d\u3002<\/p>\n<p>\u25a0 Note  recall from Chapter 4 that C# supports optional parameters. if you use optional parameters in your class constructors, you can achieve the same benefits as constructor chaining with less code. You will see how to do so in just a moment.<br \/>\n\u8bf7\u6ce8\u610f\u7b2c 4 \u7ae0\u4e2d\u7684 C# \u652f\u6301\u53ef\u9009\u53c2\u6570\u3002 \u5982\u679c\u5728\u7c7b\u6784\u9020\u51fd\u6570\u4e2d\u4f7f\u7528\u53ef\u9009\u53c2\u6570\uff0c\u5219\u53ef\u4ee5\u83b7\u5f97\u4e0e\u4f7f\u7528\u66f4\u5c11\u4ee3\u7801\u7684\u6784\u9020\u51fd\u6570\u94fe\u63a5\u76f8\u540c\u7684\u597d\u5904\u3002\u7a0d\u540e\u60a8\u5c06\u770b\u5230\u5982\u4f55\u505a\u5230\u8fd9\u4e00\u70b9\u3002<\/p>\n<h2>Observing Constructor Flow<\/h2>\n<p>\u89c2\u5bdf\u6784\u9020\u51fd\u6570\u6d41<\/p>\n<p>On a final note, do know that once a constructor passes arguments to the designated master constructor (and that constructor has processed the data), the constructor invoked originally by the caller will finish executing any remaining code statements. To clarify, update each of the constructors of the Motorcycle class with a fitting call to Console.WriteLine().<br \/>\n\u6700\u540e\u4e00\u70b9\uff0c\u8981\u77e5\u9053\uff0c\u4e00\u65e6\u6784\u9020\u51fd\u6570\u5c06\u53c2\u6570\u4f20\u9012\u7ed9\u6307\u5b9a\u7684\u4e3b\u6784\u9020\u51fd\u6570\uff08\u5e76\u4e14\u8be5\u6784\u9020\u51fd\u6570\u5df2\u7ecf\u5904\u7406\u4e86\u6570\u636e\uff09\uff0c\u8c03\u7528\u65b9\u6700\u521d\u8c03\u7528\u7684\u6784\u9020\u51fd\u6570\u5c06\u5b8c\u6210\u6267\u884c\u4efb\u4f55\u5269\u4f59\u7684\u4ee3\u7801\u8bed\u53e5\u3002\u4e3a\u4e86\u6f84\u6e05\u8fd9\u4e00\u70b9\uff0c\u8bf7\u4f7f\u7528\u5bf9 Console.WriteLine\uff08\uff09 \u7684\u5408\u9002\u8c03\u7528\u6765\u66f4\u65b0 Motorcycle \u7c7b\u7684\u6bcf\u4e2a\u6784\u9020\u51fd\u6570\u3002<\/p>\n<p>class Motorcycle<br \/>\n{<br \/>\npublic int driverIntensity; public string driverName;<\/p>\n<p>\/\/ Constructor chaining. public Motorcycle()<br \/>\n{<br \/>\nConsole.WriteLine(&quot;In default constructor&quot;);<br \/>\n}<\/p>\n<p>public Motorcycle(int intensity)<br \/>\n: this(intensity, &quot;&quot;)<br \/>\n{<br \/>\nConsole.WriteLine(&quot;In constructor taking an int&quot;);<br \/>\n}<\/p>\n<p>public Motorcycle(string name)<br \/>\n: this(0, name)<br \/>\n{<br \/>\nConsole.WriteLine(&quot;In constructor taking a string&quot;);<br \/>\n}<\/p>\n<p>\/\/ This is the 'main' constructor that does all the real work. public Motorcycle(int intensity, string name)<br \/>\n{<br \/>\nConsole.WriteLine(&quot;In main constructor&quot;); if (intensity &gt; 10)<br \/>\n{<br \/>\nintensity = 10;<br \/>\n}<\/p>\n<p>}<br \/>\n...<br \/>\n}<\/p>\n<p>driverIntensity = intensity; driverName = name;<\/p>\n<p>Now, ensure your top-level statements exercise a Motorcycle object as follows:<br \/>\n\u73b0\u5728\uff0c\u786e\u4fdd\u60a8\u7684\u9876\u7ea7\u8bed\u53e5\u6309\u5982\u4e0b\u65b9\u5f0f\u6267\u884c Motorcycle \u5bf9\u8c61\uff1a<\/p>\n<p>Console.WriteLine(&quot;<strong><strong><em> Fun with class Types <\/em><\/strong><\/strong>\\n&quot;);<\/p>\n<p>\/\/ Make a Motorcycle.<br \/>\nMotorcycle c = new Motorcycle(5); c.SetDriverName(&quot;Tiny&quot;); c.PopAWheely();<br \/>\nConsole.WriteLine(&quot;Rider name is {0}&quot;, c.driverName); Console.ReadLine();<\/p>\n<p>With this, ponder the output from the previous code:<br \/>\n\u6709\u4e86\u8fd9\u4e2a\uff0c\u601d\u8003\u524d\u9762\u4ee3\u7801\u7684\u8f93\u51fa\uff1a<\/p>\n<p><strong><strong><em> Fun with Motorcycles <\/em><\/strong><\/strong> In main constructor<br \/>\nIn constructor taking an int Yeeeeeee Haaaaaeewww!<br \/>\nYeeeeeee Haaaaaeewww! Yeeeeeee Haaaaaeewww! Yeeeeeee Haaaaaeewww! Yeeeeeee Haaaaaeewww! Yeeeeeee Haaaaaeewww! Rider name is Tiny<\/p>\n<p>As you can see, the flow of constructor logic is as follows:<br \/>\n\u5982\u60a8\u6240\u89c1\uff0c\u6784\u9020\u51fd\u6570\u903b\u8f91\u7684\u6d41\u7a0b\u5982\u4e0b\uff1a<br \/>\n\u2022   You create your object by invoking the constructor requiring a single int.<br \/>\n\u60a8\u53ef\u4ee5\u901a\u8fc7\u8c03\u7528\u9700\u8981\u5355\u4e2a int \u7684\u6784\u9020\u51fd\u6570\u6765\u521b\u5efa\u5bf9\u8c61\u3002<br \/>\n\u2022   This constructor forwards the supplied data to the master constructor and provides any additional startup arguments not specified by the caller.<br \/>\n\u6b64\u6784\u9020\u51fd\u6570\u5c06\u63d0\u4f9b\u7684\u6570\u636e\u8f6c\u53d1\u7ed9\u4e3b\u6784\u9020\u51fd\u6570\uff0c\u5e76\u63d0\u4f9b\u8c03\u7528\u65b9\u672a\u6307\u5b9a\u7684\u4efb\u4f55\u5176\u4ed6\u542f\u52a8\u53c2\u6570\u3002<br \/>\n\u2022   The master constructor assigns the incoming data to the object\u2019s field data.<br \/>\n\u4e3b\u6784\u9020\u51fd\u6570\u5c06\u4f20\u5165\u6570\u636e\u5206\u914d\u7ed9\u5bf9\u8c61\u7684\u5b57\u6bb5\u6570\u636e\u3002<br \/>\n\u2022   Control is returned to the constructor originally called and executes any remaining code statements.<br \/>\n\u63a7\u5236\u6743\u8fd4\u56de\u5230\u6700\u521d\u8c03\u7528\u7684\u6784\u9020\u51fd\u6570\uff0c\u5e76\u6267\u884c\u4efb\u4f55\u5269\u4f59\u7684\u4ee3\u7801\u8bed\u53e5\u3002<\/p>\n<p>The nice thing about using constructor chaining is that this programming pattern will work with any version of the C# language and .NET platform. However, if you are targeting .NET 4.0 and higher, you can further simplify your programming tasks by making use of optional arguments as an alternative to traditional constructor chaining.<br \/>\n\u4f7f\u7528\u6784\u9020\u51fd\u6570\u94fe\u63a5\u7684\u597d\u5904\u662f\uff0c\u6b64\u7f16\u7a0b\u6a21\u5f0f\u9002\u7528\u4e8e\u4efb\u4f55\u7248\u672c\u7684 C# \u8bed\u8a00\u548c .NET \u5e73\u53f0\u3002\u4f46\u662f\uff0c\u5982\u679c\u9762\u5411 .NET 4.0 \u53ca\u66f4\u9ad8\u7248\u672c\uff0c\u5219\u53ef\u4ee5\u901a\u8fc7\u4f7f\u7528\u53ef\u9009\u53c2\u6570\u4f5c\u4e3a\u4f20\u7edf\u6784\u9020\u51fd\u6570\u94fe\u63a5\u7684\u66ff\u4ee3\u65b9\u6cd5\u6765\u8fdb\u4e00\u6b65\u7b80\u5316\u7f16\u7a0b\u4efb\u52a1\u3002<\/p>\n<h2>Revisiting Optional Arguments<\/h2>\n<p>\u91cd\u65b0\u8bbf\u95ee\u53ef\u9009\u53c2\u6570<\/p>\n<p>In Chapter 4, you learned about optional and named arguments. Recall that optional arguments allow you to define supplied default values to incoming arguments. If the caller is happy with these defaults, they are not required to specify a unique value; however, they may do so to provide the object with custom data.<br \/>\nConsider the following version of Motorcycle, which now provides a number of ways to construct objects using a single constructor definition:<br \/>\n\u5728\u7b2c 4 \u7ae0\u4e2d\uff0c\u60a8\u4e86\u89e3\u4e86\u53ef\u9009\u53c2\u6570\u548c\u547d\u540d\u53c2\u6570\u3002\u56de\u60f3\u4e00\u4e0b\uff0c\u53ef\u9009\u53c2\u6570\u5141\u8bb8\u60a8\u5b9a\u4e49\u4e3a\u4f20\u5165\u53c2\u6570\u63d0\u4f9b\u7684\u9ed8\u8ba4\u503c\u3002\u5982\u679c\u8c03\u7528\u65b9\u5bf9\u8fd9\u4e9b\u9ed8\u8ba4\u503c\u611f\u5230\u6ee1\u610f\uff0c\u5219\u4e0d\u9700\u8981\u6307\u5b9a\u552f\u4e00\u503c;\u4f46\u662f\uff0c\u4ed6\u4eec\u8fd9\u6837\u505a\u662f\u4e3a\u4e86\u5411\u5bf9\u8c61\u63d0\u4f9b\u81ea\u5b9a\u4e49\u6570\u636e\u3002\u8bf7\u8003\u8651\u4ee5\u4e0b\u7248\u672c\u7684 Motorcycle\uff0c\u5b83\u73b0\u5728\u63d0\u4f9b\u4e86\u591a\u79cd\u4f7f\u7528\u5355\u4e2a\u6784\u9020\u51fd\u6570\u5b9a\u4e49\u6784\u9020\u5bf9\u8c61\u7684\u65b9\u6cd5\uff1a<\/p>\n<p>class Motorcycle<br \/>\n{<br \/>\n\/\/ Single constructor using optional args.<br \/>\npublic Motorcycle(int intensity = 0, string name = &quot;&quot;)<br \/>\n{<br \/>\nif (intensity &gt; 10)<br \/>\n{<br \/>\nintensity = 10;<br \/>\n}<\/p>\n<p>}<br \/>\n...<br \/>\n}<\/p>\n<p>driverIntensity = intensity; driverName = name;<\/p>\n<p>With this one constructor, you are now able to create a new Motorcycle object using zero, one, or two arguments. Recall that named argument syntax allows you to essentially skip over acceptable default settings (see Chapter 3).<br \/>\n\u6709\u4e86\u8fd9\u4e2a\u6784\u9020\u51fd\u6570\uff0c\u60a8\u73b0\u5728\u53ef\u4ee5\u4f7f\u7528\u96f6\u4e2a\u3001\u4e00\u4e2a\u6216\u4e24\u4e2a\u53c2\u6570\u521b\u5efa\u65b0\u7684 Motorcycle \u5bf9\u8c61\u3002\u56de\u60f3\u4e00\u4e0b\uff0c\u547d\u540d\u53c2\u6570\u8bed\u6cd5\u5141\u8bb8\u60a8\u57fa\u672c\u4e0a\u8df3\u8fc7\u53ef\u63a5\u53d7\u7684\u9ed8\u8ba4\u8bbe\u7f6e\uff08\u8bf7\u53c2\u9605\u7b2c 3 \u7ae0\uff09\u3002<\/p>\n<p>static void MakeSomeBikes()<br \/>\n{<br \/>\n\/\/ driverName = &quot;&quot;, driverIntensity = 0 Motorcycle m1 = new Motorcycle(); Console.WriteLine(&quot;Name= {0}, Intensity= {1}&quot;,<br \/>\nm1.driverName, m1.driverIntensity);<\/p>\n<p>\/\/ driverName = &quot;Tiny&quot;, driverIntensity = 0 Motorcycle m2 = new Motorcycle(name:&quot;Tiny&quot;); Console.WriteLine(&quot;Name= {0}, Intensity= {1}&quot;,<br \/>\nm2.driverName, m2.driverIntensity);<\/p>\n<p>\/\/ driverName = &quot;&quot;, driverIntensity = 7 Motorcycle m3 = new Motorcycle(7); Console.WriteLine(&quot;Name= {0}, Intensity= {1}&quot;,<br \/>\nm3.driverName, m3.driverIntensity);<br \/>\n}<\/p>\n<p>In any case, at this point you are able to define a class with field data (aka member variables) and various operations such as methods and constructors. Next up, let\u2019s formalize the role of the static keyword.<br \/>\n\u65e0\u8bba\u5982\u4f55\uff0c\u6b64\u65f6\u60a8\u90fd\u53ef\u4ee5\u5b9a\u4e49\u4e00\u4e2a\u5305\u542b\u5b57\u6bb5\u6570\u636e\uff08\u4e5f\u79f0\u4e3a\u6210\u5458\u53d8\u91cf\uff09\u548c\u5404\u79cd\u64cd\u4f5c\uff08\u5982\u65b9\u6cd5\u548c\u6784\u9020\u51fd\u6570\uff09\u7684\u7c7b\u3002\u63a5\u4e0b\u6765\uff0c\u8ba9\u6211\u4eec\u6b63\u5f0f\u786e\u5b9a\u9759\u6001\u5173\u952e\u5b57\u7684\u89d2\u8272\u3002<\/p>\n<h2>Understanding the static Keyword<\/h2>\n<p>\u4e86\u89e3\u9759\u6001\u5173\u952e\u5b57<br \/>\nA C# class may define any number of static members, which are declared using the static keyword. When you do so, the member in question must be invoked directly from the class level, rather than from an object reference variable. To illustrate the distinction, consider your good friend System.Console. As you have seen, you do not invoke the WriteLine() method from the object level, as shown here:<br \/>\nC# \u7c7b\u53ef\u4ee5\u5b9a\u4e49\u4efb\u610f\u6570\u91cf\u7684\u9759\u6001\u6210\u5458\uff0c\u8fd9\u4e9b\u6210\u5458\u4f7f\u7528 static \u5173\u952e\u5b57\u58f0\u660e\u3002\u6267\u884c\u6b64\u64cd\u4f5c\u65f6\uff0c\u5fc5\u987b\u76f4\u63a5\u4ece\u7c7b\u7ea7\u522b\u8c03\u7528\u76f8\u5173\u6210\u5458\uff0c\u800c\u4e0d\u662f\u4ece\u5bf9\u8c61\u5f15\u7528\u53d8\u91cf\u8c03\u7528\u3002\u4e3a\u4e86\u8bf4\u660e\u8fd9\u79cd\u533a\u522b\uff0c\u8bf7\u8003\u8651\u60a8\u7684\u597d\u670b\u53cbSystem.Console\u3002\u5982\u60a8\u6240\u89c1\uff0c\u60a8\u4e0d\u4f1a\u4ece\u5bf9\u8c61\u7ea7\u522b\u8c03\u7528 WriteLine\uff08\uff09 \u65b9\u6cd5\uff0c\u5982\u4e0b\u6240\u793a\uff1a<\/p>\n<p>\/\/ Compiler error! WriteLine() is not an object level method! Console c = new Console();<br \/>\nc.WriteLine(&quot;I can't be printed...&quot;);<\/p>\n<p>Instead, simply prefix the class name to the static WriteLine() member.<br \/>\n\u76f8\u53cd\uff0c\u53ea\u9700\u5c06\u7c7b\u540d\u524d\u7f00\u4e3a\u9759\u6001 WriteLine\uff08\uff09 \u6210\u5458\u3002<\/p>\n<p>\/\/ Correct! WriteLine() is a static method. Console.WriteLine(&quot;Much better! Thanks...&quot;);<\/p>\n<p>Simply put, static members are items that are deemed (by the class designer) to be so commonplace that there is no need to create an instance of the class before invoking the member. While any class can define static members, they are quite commonly found within utility classes. By definition, a utility class is a class that does not maintain any object-level state and is not created with the new keyword. Rather, a utility class exposes all functionality as class-level (aka static) members.<br \/>\n\u7b80\u5355\u5730\u8bf4\uff0c\u9759\u6001\u6210\u5458\u662f\uff08\u7531\u7c7b\u8bbe\u8ba1\u8005\uff09\u8ba4\u4e3a\u975e\u5e38\u5e38\u89c1\u7684\u9879\uff0c\u4ee5\u81f3\u4e8e\u5728\u8c03\u7528\u6210\u5458\u4e4b\u524d\u4e0d\u9700\u8981\u521b\u5efa\u7c7b\u7684\u5b9e\u4f8b\u3002\u867d\u7136\u4efb\u4f55\u7c7b\u90fd\u53ef\u4ee5\u5b9a\u4e49\u9759\u6001\u6210\u5458\uff0c\u4f46\u5b83\u4eec\u5728\u5b9e\u7528\u7a0b\u5e8f\u7c7b\u4e2d\u5f88\u5e38\u89c1\u3002\u6839\u636e\u5b9a\u4e49\uff0c\u5b9e\u7528\u7a0b\u5e8f\u7c7b\u662f\u4e0d\u7ef4\u62a4\u4efb\u4f55\u5bf9\u8c61\u7ea7\u72b6\u6001\u4e14\u4e0d\u4f7f\u7528 new \u5173\u952e\u5b57\u521b\u5efa\u7684\u7c7b\u3002\u76f8\u53cd\uff0c\u5b9e\u7528\u7a0b\u5e8f\u7c7b\u5c06\u6240\u6709\u529f\u80fd\u516c\u5f00\u4e3a\u7c7b\u7ea7\uff08\u4e5f\u79f0\u4e3a\u9759\u6001\uff09\u6210\u5458\u3002<\/p>\n<p>For example, if you were to use the Visual Studio Object Browser (via the View \u27a4 Object Browser menu item) to view the System namespace, you would see that all the members of the Console, Math, Environment, and GC classes (among others) expose all their functionality via static members. These are but a few utility classes found within the .NET Core base class libraries.<br \/>\n\u4f8b\u5982\uff0c\u5982\u679c\u8981\u4f7f\u7528 Visual Studio \u5bf9\u8c61\u6d4f\u89c8\u5668\uff08\u901a\u8fc7\u201c\u89c6\u56fe\u201d\u27a4\u201c\u5bf9\u8c61\u6d4f\u89c8\u5668\u201d\u83dc\u5355\u9879\uff09\u67e5\u770b\u201c\u7cfb\u7edf\u201d\u547d\u540d\u7a7a\u95f4\uff0c\u5219\u4f1a\u770b\u5230\u63a7\u5236\u53f0\u3001\u6570\u5b66\u7c7b\u3001\u73af\u5883\u548c GC \u7c7b\uff08\u4ee5\u53ca\u5176\u4ed6\u7c7b\uff09\u7684\u6240\u6709\u6210\u5458\u90fd\u901a\u8fc7\u9759\u6001\u6210\u5458\u516c\u5f00\u5176\u6240\u6709\u529f\u80fd\u3002\u8fd9\u4e9b\u53ea\u662f\u5728 .NET Core \u57fa\u7c7b\u5e93\u4e2d\u627e\u5230\u7684\u51e0\u4e2a\u5b9e\u7528\u5de5\u5177\u7c7b\u3002<\/p>\n<p>Again, be aware that static members are not only found in utility classes; they can be part of any class definition at all. Just remember that static members promote a given item to the class level rather than the object level. As you will see over the next few sections, the static keyword can be applied to the following:<br \/>\n\u540c\u6837\uff0c\u8bf7\u6ce8\u610f\uff0c\u9759\u6001\u6210\u5458\u4e0d\u4ec5\u5b58\u5728\u4e8e\u5b9e\u7528\u7a0b\u5e8f\u7c7b\u4e2d;\u5b83\u4eec\u5b8c\u5168\u53ef\u4ee5\u662f\u4efb\u4f55\u7c7b\u5b9a\u4e49\u7684\u4e00\u90e8\u5206\u3002\u8bf7\u8bb0\u4f4f\uff0c\u9759\u6001\u6210\u5458\u5c06\u7ed9\u5b9a\u9879\u76ee\u63d0\u5347\u5230\u7c7b\u7ea7\u522b\u800c\u4e0d\u662f\u5bf9\u8c61\u7ea7\u522b\u3002\u6b63\u5982\u60a8\u5c06\u5728\u63a5\u4e0b\u6765\u7684\u51e0\u8282\u4e2d\u770b\u5230\u7684\u90a3\u6837\uff0cstatic \u5173\u952e\u5b57\u53ef\u4ee5\u5e94\u7528\u4e8e\u4ee5\u4e0b\u5185\u5bb9\uff1a<\/p>\n<p>\u2022Data of a class<br \/>\n\u7c7b\u7684\u6570\u636e<br \/>\n\u2022Methods of a class<br \/>\n\u7c7b\u7684\u65b9\u6cd5<br \/>\n\u2022Properties of a class<br \/>\n\u7c7b\u7684\u5c5e\u6027<br \/>\n\u2022A constructor<br \/>\n\u6784\u9020\u51fd\u6570<br \/>\n\u2022The entire class definition<br \/>\n\u6574\u4e2a\u7c7b\u5b9a\u4e49<br \/>\n\u2022In conjunction with the C# using keyword<br \/>\n\u7ed3\u5408 C# using \u5173\u952e\u5b57<\/p>\n<p>Let\u2019s see each of our options, beginning with the concept of static data.<br \/>\n\u8ba9\u6211\u4eec\u770b\u770b\u6211\u4eec\u7684\u6bcf\u4e2a\u9009\u9879\uff0c\u4ece\u9759\u6001\u6570\u636e\u7684\u6982\u5ff5\u5f00\u59cb\u3002<\/p>\n<p>\u25a0Note You will examine the role of static properties later in this chapter while examining the properties themselves.<br \/>\n\u6ce8\u610f \u5728\u672c\u7ae0\u540e\u9762\uff0c\u60a8\u5c06\u5728\u68c0\u67e5\u5c5e\u6027\u672c\u8eab\u65f6\u68c0\u67e5\u9759\u6001\u5c5e\u6027\u7684\u4f5c\u7528\u3002<\/p>\n<h2>Defining Static Field Data<\/h2>\n<p>\u5b9a\u4e49\u9759\u6001\u5b57\u6bb5\u6570\u636e<\/p>\n<p>Most of the time when designing a class, you define data as instance-level data or, said another way, as nonstatic data. When you define instance-level data, you know that every time you create a new object, the object maintains its own independent copy of the data. In contrast, when you define static data of a class, the memory is shared by all objects of that category.<br \/>\n\u5927\u591a\u6570\u65f6\u5019\uff0c\u5728\u8bbe\u8ba1\u7c7b\u65f6\uff0c\u60a8\u5c06\u6570\u636e\u5b9a\u4e49\u4e3a\u5b9e\u4f8b\u7ea7\u6570\u636e\uff0c\u6216\u8005\u6362\u53e5\u8bdd\u8bf4\uff0c\u5b9a\u4e49\u4e3a\u975e\u9759\u6001\u6570\u636e\u3002\u5b9a\u4e49\u5b9e\u4f8b\u7ea7\u6570\u636e\u65f6\uff0c\u60a8\u77e5\u9053\u6bcf\u6b21\u521b\u5efa\u65b0\u5bf9\u8c61\u65f6\uff0c\u8be5\u5bf9\u8c61\u90fd\u4f1a\u7ef4\u62a4\u5176\u81ea\u5df1\u7684\u72ec\u7acb\u6570\u636e\u526f\u672c\u3002\u76f8\u53cd\uff0c\u5f53\u60a8\u5b9a\u4e49\u7c7b\u7684\u9759\u6001\u6570\u636e\u65f6\uff0c\u5185\u5b58\u7531\u8be5\u7c7b\u522b\u7684\u6240\u6709\u5bf9\u8c61\u5171\u4eab\u3002<\/p>\n<p>To see the distinction, create a new Console Application project named StaticDataAndMembers. Now, insert a file into your project named SavingsAccount.cs, and in that file create a new class named SavingsAccount. Begin by defining an instance-level variable (to model the current balance) and a custom constructor to set the initial balance.<br \/>\n\u82e5\u8981\u67e5\u770b\u533a\u522b\uff0c\u8bf7\u521b\u5efa\u4e00\u4e2a\u540d\u4e3a StaticDataAndMembers\u7684\u65b0\u63a7\u5236\u53f0\u5e94\u7528\u7a0b\u5e8f\u9879\u76ee\u3002\u73b0\u5728\uff0c\u5c06\u4e00\u4e2a\u540d\u4e3a Savings Account.cs \u7684\u6587\u4ef6\u63d2\u5165\u5230\u9879\u76ee\u4e2d\uff0c\u5e76\u5728\u8be5\u6587\u4ef6\u4e2d\u521b\u5efa\u4e00\u4e2a\u540d\u4e3a Savings Account \u7684\u65b0\u7c7b\u3002\u9996\u5148\u5b9a\u4e49\u4e00\u4e2a\u5b9e\u4f8b\u7ea7\u53d8\u91cf\uff08\u7528\u4e8e\u5bf9\u5f53\u524d\u4f59\u989d\u8fdb\u884c\u5efa\u6a21\uff09\u548c\u4e00\u4e2a\u81ea\u5b9a\u4e49\u6784\u9020\u51fd\u6570\u6765\u8bbe\u7f6e\u521d\u59cb\u4f59\u989d\u3002<\/p>\n<p>namespace StaticDataAndMembers;<br \/>\n\/\/ A simple savings account class. class SavingsAccount<br \/>\n{<br \/>\n\/\/ Instance-level data. public double currBalance;<br \/>\npublic SavingsAccount(double balance)<br \/>\n{<br \/>\ncurrBalance = balance;<br \/>\n}<br \/>\n}<\/p>\n<p>When you create SavingsAccount objects, memory for the currBalance field is allocated for each object. Thus, you could create five different SavingsAccount objects, each with their own unique balance. Furthermore, if you change the balance on one account, the other objects are not affected.<br \/>\n\u521b\u5efa\u50a8\u84c4\u5e10\u6237\u5bf9\u8c61\u65f6\uff0c\u5c06\u4e3a\u6bcf\u4e2a\u5bf9\u8c61\u5206\u914d currBalance \u5b57\u6bb5\u7684\u5185\u5b58\u3002\u56e0\u6b64\uff0c\u60a8\u53ef\u4ee5\u521b\u5efa\u4e94\u4e2a\u4e0d\u540c\u7684\u50a8\u84c4\u8d26\u6237\u5bf9\u8c61\uff0c\u6bcf\u4e2a\u5bf9\u8c61\u90fd\u6709\u81ea\u5df1\u72ec\u7279\u7684\u4f59\u989d\u3002\u6b64\u5916\uff0c\u5982\u679c\u60a8\u66f4\u6539\u4e00\u4e2a\u5e10\u6237\u7684\u4f59\u989d\uff0c\u5219\u5176\u4ed6\u5bf9\u8c61\u4e0d\u53d7\u5f71\u54cd\u3002<\/p>\n<p>Static data, on the other hand, is allocated once and shared among all objects of the same class category. Add a static variable named currInterestRate to the SavingsAccount class, which is set to a default value of 0.04.<br \/>\n\u53e6\u4e00\u65b9\u9762\uff0c\u9759\u6001\u6570\u636e\u5206\u914d\u4e00\u6b21\uff0c\u5e76\u5728\u540c\u4e00\u7c7b\u7c7b\u522b\u7684\u6240\u6709\u5bf9\u8c61\u4e4b\u95f4\u5171\u4eab\u3002\u5c06\u4e00\u4e2a\u540d\u4e3a currInterestRate \u7684\u9759\u6001\u53d8\u91cf\u6dfb\u52a0\u5230 Savings Account \u7c7b\uff0c\u8be5\u53d8\u91cf\u8bbe\u7f6e\u4e3a\u9ed8\u8ba4\u503c 0.04\u3002<\/p>\n<p>\/\/ A simple savings account class. class SavingsAccount<br \/>\n{<br \/>\n\/\/ A static point of data.<br \/>\npublic static double currInterestRate = 0.04;<\/p>\n<p>\/\/ Instance-level data. public double currBalance;<\/p>\n<p>public SavingsAccount(double balance)<br \/>\n{<br \/>\ncurrBalance = balance;<br \/>\n}<br \/>\n}<\/p>\n<p>Create three instances of SavingsAccount in top-level statements, as follows:<br \/>\n\u5728\u9876\u7ea7\u8bed\u53e5\u4e2d\u521b\u5efa\u4e09\u4e2a\u50a8\u84c4\u8d26\u6237\u5b9e\u4f8b\uff0c\u5982\u4e0b\u6240\u793a\uff1a<\/p>\n<p>using StaticDataAndMembers;<\/p>\n<p>Console.WriteLine(&quot;<strong><strong><em> Fun with Static Data <\/em><\/strong><\/strong>\\n&quot;); SavingsAccount s1 = new SavingsAccount(50); SavingsAccount s2 = new SavingsAccount(100); SavingsAccount s3 = new SavingsAccount(10000.75); Console.ReadLine();<br \/>\nThe in-memory data allocation would look something like Figure 5-1.<\/p>\n<p><img decoding=\"async\" src=\"\/images\/0501.jpg\" alt=\"Alt text\" \/><\/p>\n<p>Figure 5-1. Static data is allocated once and shared among all instances of the class<br \/>\n\u56fe 5-1\u3002 \u9759\u6001\u6570\u636e\u5206\u914d\u4e00\u6b21\uff0c\u5e76\u5728\u7c7b\u7684\u6240\u6709\u5b9e\u4f8b\u4e4b\u95f4\u5171\u4eab<\/p>\n<p>Here, the assumption is that all saving accounts should have the same interest rate. Because static data is shared by all objects of the same category, if you were to change it in any way, all objects will \u201csee\u201d the new value the next time they access the static data, as they are all essentially looking at the same memory location. To understand how to change (or obtain) static data, you need to consider the role of static methods.<br \/>\n\u5728\u8fd9\u91cc\uff0c\u5047\u8bbe\u6240\u6709\u50a8\u84c4\u8d26\u6237\u90fd\u5e94\u8be5\u6709\u76f8\u540c\u7684\u5229\u7387\u3002\u7531\u4e8e\u9759\u6001\u6570\u636e\u7531\u540c\u4e00\u7c7b\u522b\u7684\u6240\u6709\u5bf9\u8c61\u5171\u4eab\uff0c\u56e0\u6b64\uff0c\u5982\u679c\u8981\u4ee5\u4efb\u4f55\u65b9\u5f0f\u66f4\u6539\u5b83\uff0c\u6240\u6709\u5bf9\u8c61\u90fd\u5c06\u201c\u770b\u5230\u201d\u65b0\u7684\u503c\uff0c\u56e0\u4e3a\u5b83\u4eec\u57fa\u672c\u4e0a\u90fd\u5728\u67e5\u770b\u76f8\u540c\u7684\u5185\u5b58\u4f4d\u7f6e\u3002\u8981\u4e86\u89e3\u5982\u4f55\u66f4\u6539\uff08\u6216\u83b7\u53d6\uff09\u9759\u6001\u6570\u636e\uff0c\u9700\u8981\u8003\u8651\u9759\u6001\u65b9\u6cd5\u7684\u4f5c\u7528\u3002<\/p>\n<h2>Defining Static Methods<\/h2>\n<p>\u5b9a\u4e49\u9759\u6001\u65b9\u6cd5<\/p>\n<p>Let\u2019s update the SavingsAccount class to define two static methods. The first static method (GetInterestRate()) will return the current interest rate, while the second static method (SetInterestRate()) will allow you to change the interest rate.<br \/>\n\u8ba9\u6211\u4eec\u66f4\u65b0 Savings Account \u7c7b\u4ee5\u5b9a\u4e49\u4e24\u4e2a\u9759\u6001\u65b9\u6cd5\u3002\u7b2c\u4e00\u4e2a\u9759\u6001\u65b9\u6cd5\uff08GetInterestRate\uff08\uff09\uff09\u5c06\u8fd4\u56de\u5f53\u524d\u5229\u7387\uff0c\u800c\u7b2c\u4e8c\u4e2a\u9759\u6001\u65b9\u6cd5\uff08SetInterestRate\uff08\uff09\uff09\u5c06\u5141\u8bb8\u60a8\u66f4\u6539\u5229\u7387\u3002<\/p>\n<p>\/\/ A simple savings account class. class SavingsAccount<br \/>\n{<br \/>\n\/\/ Instance-level data. public double currBalance;<\/p>\n<p>\/\/ A static point of data.<br \/>\npublic static double currInterestRate = 0.04;<\/p>\n<p>public SavingsAccount(double balance)<br \/>\n{<br \/>\ncurrBalance = balance;<br \/>\n}<\/p>\n<p>\/\/ Static members to get\/set interest rate.<br \/>\npublic static void SetInterestRate(double newRate)<br \/>\n=&gt; currInterestRate = newRate;<\/p>\n<p>public static double GetInterestRate()<br \/>\n=&gt; currInterestRate;<br \/>\n}<\/p>\n<p>Now, observe the following usage:<br \/>\n\u73b0\u5728\uff0c\u89c2\u5bdf\u4ee5\u4e0b\u7528\u6cd5\uff1a<\/p>\n<p>using StaticDataAndMembers;<\/p>\n<p>Console.WriteLine(&quot;<strong><strong><em> Fun with Static Data <\/em><\/strong><\/strong>\\n&quot;); SavingsAccount s1 = new SavingsAccount(50); SavingsAccount s2 = new SavingsAccount(100);<\/p>\n<p>\/\/ Print the current interest rate.<br \/>\nConsole.WriteLine(&quot;Interest Rate is: {0}&quot;, SavingsAccount.GetInterestRate());<\/p>\n<p>\/\/ Make new object, this does NOT 'reset' the interest rate. SavingsAccount s3 = new SavingsAccount(10000.75);<br \/>\nConsole.WriteLine(&quot;Interest Rate is: {0}&quot;, SavingsAccount.GetInterestRate()); Console.ReadLine();<\/p>\n<p>The output of the previous code is shown here:<br \/>\n\u524d\u9762\u4ee3\u7801\u7684\u8f93\u51fa\u5982\u4e0b\u6240\u793a\uff1a<\/p>\n<p><strong><strong><em> Fun with Static Data <\/em><\/strong><\/strong> Interest Rate is: 0.04<br \/>\nInterest Rate is: 0.04<\/p>\n<p>As you can see, when you create new instances of the SavingsAccount class, the value of the static data is not reset, as the CoreCLR will allocate the static data into memory exactly one time. After that point, all objects of type SavingsAccount operate on the same value for the static currInterestRate field.<br \/>\n\u5982\u60a8\u6240\u89c1\uff0c\u5f53\u60a8\u521b\u5efa Savings Account \u7c7b\u7684\u65b0\u5b9e\u4f8b\u65f6\uff0c\u9759\u6001\u6570\u636e\u7684\u503c\u4e0d\u4f1a\u91cd\u7f6e\uff0c\u56e0\u4e3a CoreCLR \u4f1a\u5c06\u9759\u6001\u6570\u636e\u7cbe\u786e\u5206\u914d\u5230\u5185\u5b58\u4e2d\u4e00\u6b21\u3002\u5728\u6b64\u4e4b\u540e\uff0c\u7c7b\u578b\u4e3a\u201c\u50a8\u84c4\u8d26\u6237\u201d\u7684\u6240\u6709\u5bf9\u8c61\u5bf9\u9759\u6001 currInterest \u5b57\u6bb5\u7684\u76f8\u540c\u503c\u8fdb\u884c\u64cd\u4f5c\u3002<\/p>\n<p>When designing any C# class, one of your design challenges is to determine which pieces of data should be defined as static members and which should not. While there are no hard-and-fast rules, remember that a static data field is shared by all objects of that type. Therefore, if you are defining a point of data that all objects should share between them, static is the way to go.<br \/>\n\u5728\u8bbe\u8ba1\u4efb\u4f55 C# \u7c7b\u65f6\uff0c\u8bbe\u8ba1\u96be\u9898\u4e4b\u4e00\u662f\u786e\u5b9a\u54ea\u4e9b\u6570\u636e\u6bb5\u5e94\u5b9a\u4e49\u4e3a\u9759\u6001\u6210\u5458\uff0c\u54ea\u4e9b\u4e0d\u5e94\u5b9a\u4e49\u3002\u867d\u7136\u6ca1\u6709\u786c\u6027\u89c4\u5b9a\uff0c\u4f46\u8bf7\u8bb0\u4f4f\uff0c\u9759\u6001\u6570\u636e\u5b57\u6bb5\u7531\u8be5\u7c7b\u578b\u7684\u6240\u6709\u5bf9\u8c61\u5171\u4eab\u3002\u56e0\u6b64\uff0c\u5982\u679c\u8981\u5b9a\u4e49\u6240\u6709\u5bf9\u8c61\u4e4b\u95f4\u5e94\u5171\u4eab\u7684\u6570\u636e\u70b9\uff0c\u5219\u9759\u6001\u662f\u8981\u8d70\u7684\u8def\u3002<\/p>\n<p>Consider what would happen if the interest rate variable were not defined using the static keyword. This would mean every SavingsAccount object would have its own copy of the currInterestRate field. Now, assume you created 100 SavingsAccount objects and needed to change the interest rate. That would require you to call the SetInterestRate() method 100 times! Clearly, this would not be a useful way to model \u201cshared data.\u201d Again, static data is perfect when you have a value that should be common to all objects of that category.<br \/>\n\u8003\u8651\u4e00\u4e0b\u5982\u679c\u4e0d\u4f7f\u7528 static \u5173\u952e\u5b57\u5b9a\u4e49\u5229\u7387\u53d8\u91cf\u4f1a\u53d1\u751f\u4ec0\u4e48\u60c5\u51b5\u3002\u8fd9\u610f\u5473\u7740\u6bcf\u4e2a\u50a8\u84c4\u8d26\u6237\u5bf9\u8c61\u90fd\u6709\u81ea\u5df1\u7684currInterestRate\u5b57\u6bb5\u526f\u672c\u3002\u73b0\u5728\uff0c\u5047\u8bbe\u60a8\u521b\u5efa\u4e86 100 \u4e2a\u50a8\u84c4\u8d26\u6237\u5bf9\u8c61\uff0c\u5e76\u4e14\u9700\u8981\u66f4\u6539\u5229\u7387\u3002\u8fd9\u5c06\u9700\u8981\u60a8\u8c03\u7528 SetInterestRate\uff08\uff09 \u65b9\u6cd5100 \u6b21\uff01\u663e\u7136\uff0c\u8fd9\u4e0d\u662f\u5bf9\u201c\u5171\u4eab\u6570\u636e\u201d\u8fdb\u884c\u5efa\u6a21\u7684\u6709\u7528\u65b9\u6cd5\u3002\u540c\u6837\uff0c\u5f53\u60a8\u5177\u6709\u8be5\u7c7b\u522b\u7684\u6240\u6709\u5bf9\u8c61\u90fd\u5e94\u8be5\u901a\u7528\u7684\u503c\u65f6\uff0c\u9759\u6001\u6570\u636e\u662f\u5b8c\u7f8e\u7684\u3002<\/p>\n<p>\u25a0 Note  it is a compiler error for a static member to reference nonstatic members in its implementation. on a related note, it is an error to use the this keyword on a static member because this implies an object!<br \/>\n\u8bf7\u6ce8\u610f\uff0c\u9759\u6001\u6210\u5458\u5728\u5176\u5b9e\u73b0\u4e2d\u5f15\u7528\u975e\u9759\u6001\u6210\u5458\u662f\u4e00\u4e2a\u7f16\u8bd1\u5668\u9519\u8bef\u3002\u5728\u76f8\u5173\u7684\u8bf4\u660e\u4e2d\uff0c\u5728\u9759\u6001\u6210\u5458\u4e0a\u4f7f\u7528 this \u5173\u952e\u5b57\u662f\u9519\u8bef\u7684\uff0c\u56e0\u4e3a\u8fd9\u610f\u5473\u7740\u4e00\u4e2a\u5bf9\u8c61\uff01<\/p>\n<h2>Defining Static Constructors<\/h2>\n<p>\u5b9a\u4e49\u9759\u6001\u6784\u9020\u51fd\u6570<\/p>\n<p>A typical constructor is used to set the value of an object\u2019s instance-level data at the time of creation. However, what would happen if you attempted to assign the value of a static point of data in a typical constructor? You might be surprised to find that the value is reset each time you create a new object.<br \/>\n\u5178\u578b\u7684\u6784\u9020\u51fd\u6570\u7528\u4e8e\u5728\u521b\u5efa\u65f6\u8bbe\u7f6e\u5bf9\u8c61\u7684\u5b9e\u4f8b\u7ea7\u6570\u636e\u7684\u503c\u3002\u4f46\u662f\uff0c\u5982\u679c\u60a8\u5c1d\u8bd5\u5728\u5178\u578b\u6784\u9020\u51fd\u6570\u4e2d\u5206\u914d\u9759\u6001\u6570\u636e\u70b9\u7684\u503c\uff0c\u4f1a\u53d1\u751f\u4ec0\u4e48\u60c5\u51b5\uff1f\u60a8\u53ef\u80fd\u4f1a\u60ca\u8bb6\u5730\u53d1\u73b0\uff0c\u6bcf\u6b21\u521b\u5efa\u65b0\u5bf9\u8c61\u65f6\uff0c\u8be5\u503c\u90fd\u4f1a\u91cd\u7f6e\u3002<\/p>\n<p>To illustrate, assume you have updated the SavingsAccount class constructor as follows (also note you are no longer assigning the currInterestRate field inline):<br \/>\n\u4e3a\u4e86\u8bf4\u660e\u8fd9\u4e00\u70b9\uff0c\u5047\u8bbe\u60a8\u5df2\u6309\u5982\u4e0b\u65b9\u5f0f\u66f4\u65b0\u4e86 Savings Account \u7c7b\u6784\u9020\u51fd\u6570\uff08\u53e6\u8bf7\u6ce8\u610f\uff0c\u60a8\u4e0d\u518d\u4ee5\u5185\u8054\u65b9\u5f0f\u5206\u914d currInterestRate \u5b57\u6bb5\uff09\uff1a<\/p>\n<p>class SavingsAccount<br \/>\n{<br \/>\npublic double currBalance;<br \/>\npublic static double currInterestRate;<\/p>\n<p>\/\/ Notice that our constructor is setting<br \/>\n\/\/ the static currInterestRate value. public SavingsAccount(double balance)<br \/>\n{<\/p>\n<p>}<br \/>\n...<br \/>\n}<\/p>\n<p>currInterestRate = 0.04; \/\/ This is static data! currBalance = balance;<\/p>\n<p>Now, assume you have authored the following code in the top-level statements:<br \/>\n\u73b0\u5728\uff0c\u5047\u8bbe\u60a8\u5df2\u5728\u9876\u7ea7\u8bed\u53e5\u4e2d\u521b\u4f5c\u4e86\u4ee5\u4e0b\u4ee3\u7801\uff1a<\/p>\n<p>\/\/ Make an account.<br \/>\nSavingsAccount s1 = new SavingsAccount(50);<\/p>\n<p>\/\/ Print the current interest rate.<br \/>\nConsole.WriteLine(&quot;Interest Rate is: {0}&quot;, SavingsAccount.GetInterestRate());<\/p>\n<p>\/\/ Try to change the interest rate via property. SavingsAccount.SetInterestRate(0.08);<\/p>\n<p>\/\/ Make a second account.<br \/>\nSavingsAccount s2 = new SavingsAccount(100);<\/p>\n<p>\/\/ Should print 0.08...right??<br \/>\nConsole.WriteLine(&quot;Interest Rate is: {0}&quot;, SavingsAccount.GetInterestRate()); Console.ReadLine();<\/p>\n<p>If you executed the previous code, you would see that the currInterestRate variable is reset each time you create a new SavingsAccount object, and it is always set to 0.04. Clearly, setting the value of static data in a normal instance-level constructor sort of defeats the whole purpose. Every time you make a new object, the class-level data is reset. One approach to setting a static field is to use member initialization syntax, as you did originally.<br \/>\n\u5982\u679c\u6267\u884c\u524d\u9762\u7684\u4ee3\u7801\uff0c\u5219\u6bcf\u6b21\u521b\u5efa\u65b0\u7684 Savings Account \u5bf9\u8c61\u65f6\uff0c\u90fd\u4f1a\u91cd\u7f6e currInterestRate \u53d8\u91cf\uff0c\u5e76\u4e14\u8be5\u53d8\u91cf\u59cb\u7ec8\u8bbe\u7f6e\u4e3a 0.04\u3002\u663e\u7136\uff0c\u5728\u666e\u901a\u7684\u5b9e\u4f8b\u7ea7\u6784\u9020\u51fd\u6570\u4e2d\u8bbe\u7f6e\u9759\u6001\u6570\u636e\u7684\u503c\u6709\u70b9\u8fdd\u80cc\u4e86\u6574\u4e2a\u76ee\u7684\u3002\u6bcf\u6b21\u521b\u5efa\u65b0\u5bf9\u8c61\u65f6\uff0c\u90fd\u4f1a\u91cd\u7f6e\u7c7b\u7ea7\u6570\u636e\u3002\u8bbe\u7f6e\u9759\u6001\u5b57\u6bb5\u7684\u4e00\u79cd\u65b9\u6cd5\u662f\u4f7f\u7528\u6210\u5458\u521d\u59cb\u5316\u8bed\u6cd5\uff0c\u5c31\u50cf\u60a8\u6700\u521d\u6240\u505a\u7684\u90a3\u6837\u3002<\/p>\n<p>class SavingsAccount<br \/>\n{<br \/>\npublic double currBalance;<\/p>\n<p>\/\/ A static point of data.<br \/>\npublic static double currInterestRate = 0.04;<br \/>\n...<br \/>\n}<\/p>\n<p>This approach will ensure the static field is assigned only once, regardless of how many objects you create. However, what if the value for your static data needed to be obtained at runtime? For example, in a typical banking application, the value of an interest rate variable would be read from a database or external file. Performing such tasks usually requires a method scope such as a constructor to execute the code statements.<br \/>\n\u6b64\u65b9\u6cd5\u5c06\u786e\u4fdd\u9759\u6001\u5b57\u6bb5\u53ea\u5206\u914d\u4e00\u6b21\uff0c\u65e0\u8bba\u60a8\u521b\u5efa\u591a\u5c11\u4e2a\u5bf9\u8c61\u3002\u4f46\u662f\uff0c\u5982\u679c\u9700\u8981\u5728\u8fd0\u884c\u65f6\u83b7\u53d6\u9759\u6001\u6570\u636e\u7684\u503c\uff0c\u8be5\u600e\u4e48\u529e\uff1f\u4f8b\u5982\uff0c\u5728\u5178\u578b\u7684\u94f6\u884c\u5e94\u7528\u7a0b\u5e8f\u4e2d\uff0c\u5c06\u4ece\u6570\u636e\u5e93\u6216\u5916\u90e8\u6587\u4ef6\u4e2d\u8bfb\u53d6\u5229\u7387\u53d8\u91cf\u7684\u503c\u3002\u6267\u884c\u6b64\u7c7b\u4efb\u52a1\u901a\u5e38\u9700\u8981\u65b9\u6cd5\u8303\u56f4\uff08\u5982\u6784\u9020\u51fd\u6570\uff09\u6765\u6267\u884c\u4ee3\u7801\u8bed\u53e5\u3002<\/p>\n<p>For this reason, C# allows you to define a static constructor, which allows you to safely set the values of your static data. Consider the following update to your class:<br \/>\n\u56e0\u6b64\uff0cC# \u5141\u8bb8\u60a8\u5b9a\u4e49\u9759\u6001\u6784\u9020\u51fd\u6570\uff0c\u4ece\u800c\u53ef\u4ee5\u5b89\u5168\u5730\u8bbe\u7f6e\u9759\u6001\u6570\u636e\u7684\u503c\u3002\u8bf7\u8003\u8651\u5bf9\u7c7b\u8fdb\u884c\u4ee5\u4e0b\u66f4\u65b0\uff1a<\/p>\n<p>class SavingsAccount<br \/>\n{<br \/>\npublic double currBalance;<br \/>\npublic static double currInterestRate;<\/p>\n<p>public SavingsAccount(double balance)<br \/>\n{<br \/>\ncurrBalance = balance;<br \/>\n}<\/p>\n<p>\/\/ A static constructor!<br \/>\nstatic SavingsAccount()<br \/>\n{<\/p>\n<p>}<br \/>\n...<br \/>\n}<\/p>\n<p>Console.WriteLine(&quot;In static constructor!&quot;); currInterestRate = 0.04;<\/p>\n<p>Simply put, a static constructor is a special constructor that is an ideal place to initialize the values of static data when the value is not known at compile time (e.g., you need to read in the value from an external file, read in the value from a database, generate a random number, or whatnot). If you were to rerun the previous code, you would find the output you expect. Note that the message \u201cIn static constructor!\u201d prints only one time, as the CoreCLR calls all static constructors before the first use (and never calls them again for that instance of the application).<br \/>\n\u7b80\u5355\u5730\u8bf4\uff0c\u9759\u6001\u6784\u9020\u51fd\u6570\u662f\u4e00\u4e2a\u7279\u6b8a\u7684\u6784\u9020\u51fd\u6570\uff0c\u5f53\u503c\u5728\u7f16\u8bd1\u65f6\u672a\u77e5\u65f6\uff0c\u5b83\u662f\u521d\u59cb\u5316\u9759\u6001\u6570\u636e\u503c\u7684\u7406\u60f3\u4f4d\u7f6e\uff08\u4f8b\u5982\uff0c\u60a8\u9700\u8981\u4ece\u5916\u90e8\u6587\u4ef6\u4e2d\u8bfb\u53d6\u503c\uff0c\u4ece\u6570\u636e\u5e93\u4e2d\u8bfb\u53d6\u503c\uff0c\u751f\u6210\u968f\u673a\u6570\uff0c\u7b49\u7b49\uff09\u3002\u5982\u679c\u8981\u91cd\u65b0\u8fd0\u884c\u524d\u9762\u7684\u4ee3\u7801\uff0c\u5219\u4f1a\u627e\u5230\u6240\u9700\u7684\u8f93\u51fa\u3002\u8bf7\u6ce8\u610f\uff0c\u6d88\u606f\u201cIn static constructor\uff01\u201d\u53ea\u6253\u5370\u4e00\u6b21\uff0c\u56e0\u4e3a CoreCLR \u5728\u9996\u6b21\u4f7f\u7528\u4e4b\u524d\u8c03\u7528\u6240\u6709\u9759\u6001\u6784\u9020\u51fd\u6570\uff08\u5e76\u4e14\u6c38\u8fdc\u4e0d\u4f1a\u4e3a\u8be5\u5e94\u7528\u7a0b\u5e8f\u7684\u8be5\u5b9e\u4f8b\u518d\u6b21\u8c03\u7528\u5b83\u4eec\uff09\u3002<\/p>\n<p><strong><strong><em> Fun with Static Data <\/em><\/strong><\/strong> In static constructor!<br \/>\nInterest Rate is: 0.04 Interest Rate is: 0.08<\/p>\n<p>Here are a few points of interest regarding static constructors:<br \/>\n\u4ee5\u4e0b\u662f\u6709\u5173\u9759\u6001\u6784\u9020\u51fd\u6570\u7684\u4e00\u4e9b\u5173\u6ce8\u70b9\uff1a<\/p>\n<p>\u2022A given class may define only a single static constructor. In other words, the static constructor cannot be overloaded.<br \/>\n\u7ed9\u5b9a\u7684\u7c7b\u53ea\u80fd\u5b9a\u4e49\u5355\u4e2a\u9759\u6001\u6784\u9020\u51fd\u6570\u3002\u6362\u53e5\u8bdd\u8bf4\uff0c\u9759\u6001\u6784\u9020\u51fd\u6570\u4e0d\u80fd\u91cd\u8f7d\u3002<br \/>\n\u2022A static constructor does not take an access modifier and cannot take any parameters.<br \/>\n\u9759\u6001\u6784\u9020\u51fd\u6570\u4e0d\u91c7\u7528\u8bbf\u95ee\u4fee\u9970\u7b26\uff0c\u4e5f\u4e0d\u80fd\u91c7\u7528\u4efb\u4f55\u53c2\u6570\u3002<br \/>\n\u2022A static constructor executes exactly one time, regardless of how many objects of the type are created.<br \/>\n\u9759\u6001\u6784\u9020\u51fd\u6570\u53ea\u6267\u884c\u4e00\u6b21\uff0c\u800c\u4e0d\u8003\u8651\u521b\u5efa\u4e86\u591a\u5c11\u7c7b\u578b\u7684\u5bf9\u8c61\u3002<br \/>\n\u2022The runtime invokes the static constructor when it creates an instance of the class or before accessing the first static member invoked by the caller.<br \/>\n\u8fd0\u884c\u65f6\u5728\u521b\u5efa\u7c7b\u7684\u5b9e\u4f8b\u65f6\u6216\u5728\u8bbf\u95ee\u8c03\u7528\u65b9\u8c03\u7528\u7684\u7b2c\u4e00\u4e2a\u9759\u6001\u6210\u5458\u4e4b\u524d\u8c03\u7528\u9759\u6001\u6784\u9020\u51fd\u6570\u3002<br \/>\n\u2022The static constructor executes before any instance-level constructors.<br \/>\n\u9759\u6001\u6784\u9020\u51fd\u6570\u5728\u4efb\u4f55\u5b9e\u4f8b\u7ea7\u6784\u9020\u51fd\u6570\u4e4b\u524d\u6267\u884c\u3002<\/p>\n<p>Given this modification, when you create new SavingsAccount objects, the value of the static data is preserved, as the static member is set only one time within the static constructor, regardless of the number of objects created.<br \/>\n\u6839\u636e\u6b64\u4fee\u6539\uff0c\u5f53\u60a8\u521b\u5efa\u65b0\u7684 Savings Account \u5bf9\u8c61\u65f6\uff0c\u5c06\u4fdd\u7559\u9759\u6001\u6570\u636e\u7684\u503c\uff0c\u56e0\u4e3a\u9759\u6001\u6210\u5458\u5728\u9759\u6001\u6784\u9020\u51fd\u6570\u4e2d\u4ec5\u8bbe\u7f6e\u4e00\u6b21\uff0c\u800c\u4e0d\u8003\u8651\u521b\u5efa\u7684\u5bf9\u8c61\u6570\u3002<\/p>\n<h2>Defining Static Classes<\/h2>\n<p>\u5b9a\u4e49\u9759\u6001\u7c7b<\/p>\n<p>It is also possible to apply the static keyword directly on the class level. When a class has been defined as static, it is not creatable using the new keyword, and it can contain only members or data fields marked with the static keyword. If this is not the case, you receive compiler errors.<br \/>\n\u4e5f\u53ef\u4ee5\u76f4\u63a5\u5728\u7c7b\u7ea7\u522b\u5e94\u7528 static \u5173\u952e\u5b57\u3002\u5f53\u7c7b\u88ab\u5b9a\u4e49\u4e3a\u9759\u6001\u65f6\uff0c\u5b83\u4e0d\u80fd\u4f7f\u7528 new \u5173\u952e\u5b57\u521b\u5efa\uff0c\u5e76\u4e14\u5b83\u53ea\u80fd\u5305\u542b\u7528 static \u5173\u952e\u5b57\u6807\u8bb0\u7684\u6210\u5458\u6216\u6570\u636e\u5b57\u6bb5\u3002\u5982\u679c\u4e0d\u662f\u8fd9\u79cd\u60c5\u51b5\uff0c\u60a8\u5c06\u6536\u5230\u7f16\u8bd1\u5668\u9519\u8bef\u3002<\/p>\n<p>\u25a0 Note  recall that a class (or structure) that exposes only static functionality is often termed a utility class. When designing a utility class, it is good practice to apply the static keyword to the class definition.<br \/>\n\u8bf7\u6ce8\u610f\uff0c\u4ec5\u516c\u5f00\u9759\u6001\u529f\u80fd\u7684\u7c7b\uff08\u6216\u7ed3\u6784\uff09\u901a\u5e38\u79f0\u4e3a\u5b9e\u7528\u7a0b\u5e8f\u7c7b\u3002\u8bbe\u8ba1\u5b9e\u7528\u7a0b\u5e8f\u7c7b\u65f6\uff0c\u6700\u597d\u5c06 static \u5173\u952e\u5b57\u5e94\u7528\u4e8e\u7c7b\u5b9a\u4e49\u3002<\/p>\n<p>At first glance, this might seem like a fairly odd feature, given that a class that cannot be created does not appear all that helpful. However, if you create a class that contains nothing but static members and\/or constant data, the class has no need to be allocated in the first place! To illustrate, create a new class named TimeUtilClass and define it as follows:<br \/>\n\u4e4d\u4e00\u770b\uff0c\u8fd9\u4f3c\u4e4e\u662f\u4e00\u4e2a\u76f8\u5f53\u5947\u602a\u7684\u529f\u80fd\uff0c\u56e0\u4e3a\u65e0\u6cd5\u521b\u5efa\u7684\u7c7b\u4f3c\u4e4e\u5e76\u6ca1\u6709\u90a3\u4e48\u6709\u7528\u3002\u4f46\u662f\uff0c\u5982\u679c\u60a8\u521b\u5efa\u4e00\u4e2a\u53ea\u5305\u542b\u9759\u6001\u6210\u5458\u548c\/\u6216\u5e38\u91cf\u6570\u636e\u7684\u7c7b\uff0c\u5219\u9996\u5148\u4e0d\u9700\u8981\u5206\u914d\u8be5\u7c7b\uff01\u4e3a\u4e86\u8bf4\u660e\u8fd9\u4e00\u70b9\uff0c\u8bf7\u521b\u5efa\u4e00\u4e2a\u540d\u4e3a TimeUtilClass \u7684\u65b0\u7c7b\uff0c\u5e76\u6309\u5982\u4e0b\u65b9\u5f0f\u5b9a\u4e49\u5b83\uff1a<\/p>\n<p>namespace StaticDataAndMembers;<br \/>\n\/\/ Static classes can only<br \/>\n\/\/ contain static members! static class TimeUtilClass<\/p>\n<p>{<br \/>\npublic static void PrintTime()<br \/>\n=&gt; Console.WriteLine(DateTime.Now.ToShortTimeString());<\/p>\n<p>public static void PrintDate()<br \/>\n=&gt; Console.WriteLine(DateTime.Today.ToShortDateString());<br \/>\n}<\/p>\n<p>Given that this class has been defined with the static keyword, you cannot create an instance of the TimeUtilClass using the new keyword. Rather, all functionality is exposed from the class level. To test this class, add the following to the top-level statements:<br \/>\n\u9274\u4e8e\u6b64\u7c7b\u5df2\u4f7f\u7528 static \u5173\u952e\u5b57\u5b9a\u4e49\uff0c\u5219\u65e0\u6cd5\u4f7f\u7528 new \u5173\u952e\u5b57\u521b\u5efa TimeUtilClass \u7684\u5b9e\u4f8b\u3002\u76f8\u53cd\uff0c\u6240\u6709\u529f\u80fd\u90fd\u662f\u4ece\u7c7b\u7ea7\u522b\u516c\u5f00\u7684\u3002\u82e5\u8981\u6d4b\u8bd5\u6b64\u7c7b\uff0c\u8bf7\u5c06\u4ee5\u4e0b\u5185\u5bb9\u6dfb\u52a0\u5230\u9876\u7ea7\u8bed\u53e5\u4e2d\uff1a<\/p>\n<p>\/\/ These compile just fine. TimeUtilClass.PrintDate(); TimeUtilClass.PrintTime();<\/p>\n<p>\/\/ Compiler error! Can't create instance of static classes!<br \/>\nTimeUtilClass u = new TimeUtilClass(); Console.ReadLine();<\/p>\n<h2>Importing Static Members via the C# using Keyword<\/h2>\n<p>\u4f7f\u7528 Keyword \u901a\u8fc7 C# \u5bfc\u5165\u9759\u6001\u6210\u5458<\/p>\n<p>C# 6 added support for importing static members with the using keyword. To illustrate, consider the C# file currently defining the utility class. Because you are making calls to the WriteLine() method of the Console class, as well as the Now and Today properties of the DateTime class, you must have a using statement for the System namespace. Since the members of these classes are all static, you could alter your code file with the following static using directives:<br \/>\nC# 6 \u6dfb\u52a0\u4e86\u5bf9\u4f7f\u7528 using \u5173\u952e\u5b57\u5bfc\u5165\u9759\u6001\u6210\u5458\u7684\u652f\u6301\u3002\u4e3a\u4e86\u8fdb\u884c\u8bf4\u660e\uff0c\u8bf7\u8003\u8651\u5f53\u524d\u5b9a\u4e49\u5b9e\u7528\u5de5\u5177\u7c7b\u7684 C# \u6587\u4ef6\u3002\u7531\u4e8e\u8981\u8c03\u7528 Console \u7c7b\u7684 WriteLine\uff08\uff09 \u65b9\u6cd5\u4ee5\u53ca DateTime \u7c7b\u7684 Now \u548c Today \u5c5e\u6027\uff0c\u56e0\u6b64\u5fc5\u987b\u5177\u6709 System \u547d\u540d\u7a7a\u95f4\u7684 using \u8bed\u53e5\u3002\u7531\u4e8e\u8fd9\u4e9b\u7c7b\u7684\u6210\u5458\u90fd\u662f\u9759\u6001\u7684\uff0c\u56e0\u6b64\u53ef\u4ee5\u4f7f\u7528\u4ee5\u4e0b\u9759\u6001 using \u6307\u4ee4\u66f4\u6539\u4ee3\u7801\u6587\u4ef6\uff1a<\/p>\n<p>\/\/ Import the static members of Console and DateTime.<br \/>\n\/\/ \u5bfc\u5165\u63a7\u5236\u53f0\u548c\u65e5\u671f\u65f6\u95f4\u7684\u9759\u6001\u6210\u5458\u3002<br \/>\nusing static System.Console;<br \/>\nusing static System.DateTime;<\/p>\n<p>With these \u201cstatic imports,\u201d the remainder of your code file is able to directly use the static members of the Console and DateTime classes, without the need to prefix the defining class. For example, you could update your utility class like so:<br \/>\n\u901a\u8fc7\u8fd9\u4e9b\u201c\u9759\u6001\u5bfc\u5165\u201d\uff0c\u4ee3\u7801\u6587\u4ef6\u7684\u5176\u4f59\u90e8\u5206\u53ef\u4ee5\u76f4\u63a5\u4f7f\u7528 Console \u548c DateTime \u7c7b\u7684\u9759\u6001\u6210\u5458\uff0c\u800c\u65e0\u9700\u4e3a\u5b9a\u4e49\u7c7b\u6dfb\u52a0\u524d\u7f00\u3002\u4f8b\u5982\uff0c\u60a8\u53ef\u4ee5\u50cf\u8fd9\u6837\u66f4\u65b0\u5b9e\u7528\u7a0b\u5e8f\u7c7b\uff1a<\/p>\n<p>static class TimeUtilClass<br \/>\n{<br \/>\npublic static void PrintTime()<br \/>\n=&gt; WriteLine(Now.ToShortTimeString());<\/p>\n<p>public static void PrintDate()<br \/>\n=&gt; WriteLine(Today.ToShortDateString());<br \/>\n}<\/p>\n<p>A more realistic example of code simplification with importing static members might involve a C# class that is making substantial use of the System.Math class (or some other utility class). Since this class has nothing but static members, it could be somewhat easier to have a static using statement for this type and then directly call into the members of the Math class in your code file.<br \/>\n\u4f7f\u7528\u5bfc\u5165\u9759\u6001\u6210\u5458\u7b80\u5316\u4ee3\u7801\u7684\u4e00\u4e2a\u66f4\u5b9e\u9645\u7684\u793a\u4f8b\u53ef\u80fd\u6d89\u53ca\u5927\u91cf\u4f7f\u7528 System.Math \u7c7b\uff08\u6216\u67d0\u4e2a\u5176\u4ed6\u5b9e\u7528\u5de5\u5177\u7c7b\uff09\u7684 C# \u7c7b\u3002\u7531\u4e8e\u6b64\u7c7b\u53ea\u6709\u9759\u6001\u6210\u5458\uff0c\u56e0\u6b64\u4e3a\u8fd9\u79cd\u7c7b\u578b\u7684\u9759\u6001 using \u8bed\u53e5\u4f7f\u7528\u9759\u6001\u8bed\u53e5\uff0c\u7136\u540e\u76f4\u63a5\u8c03\u7528\u4ee3\u7801\u6587\u4ef6\u4e2d Math \u7c7b\u7684\u6210\u5458\u53ef\u80fd\u4f1a\u66f4\u5bb9\u6613\u4e00\u4e9b\u3002<\/p>\n<p>However, be aware that overuse of static import statements could result in potential confusion. First, what if multiple classes define a WriteLine() method? The compiler is confused and so are others reading your code. Second, unless developers are familiar with the .NET Core code libraries, they might not know that WriteLine() is a member of the Console class. Unless people were to notice the set of static imports at the top of a C# code file, they might be quite unsure where this method is actually defined. For these reasons, I will limit the use of static using statements in this text.<br \/>\n\u4f46\u662f\uff0c\u8bf7\u6ce8\u610f\uff0c\u8fc7\u5ea6\u4f7f\u7528\u9759\u6001\u5bfc\u5165\u8bed\u53e5\u53ef\u80fd\u4f1a\u5bfc\u81f4\u6f5c\u5728\u7684\u6df7\u6dc6\u3002\u9996\u5148\uff0c\u5982\u679c\u591a\u4e2a\u7c7b\u5b9a\u4e49\u4e00\u4e2a WriteLine\uff08\uff09 \u65b9\u6cd5\u600e\u4e48\u529e\uff1f\u7f16\u8bd1\u5668\u5f88\u56f0\u60d1\uff0c\u5176\u4ed6\u4eba\u4e5f\u5728\u9605\u8bfb\u60a8\u7684\u4ee3\u7801\u3002\u5176\u6b21\uff0c\u9664\u975e\u5f00\u53d1\u4eba\u5458\u719f\u6089 .NET Core \u4ee3\u7801\u5e93\uff0c\u5426\u5219\u4ed6\u4eec\u53ef\u80fd\u4e0d\u77e5\u9053 WriteLine\uff08\uff09 \u662f Console \u7c7b\u7684\u6210\u5458\u3002\u9664\u975e\u4eba\u4eec\u6ce8\u610f\u5230 C# \u4ee3\u7801\u6587\u4ef6\u9876\u90e8\u7684\u9759\u6001\u5bfc\u5165\u96c6\uff0c\u5426\u5219\u4ed6\u4eec\u53ef\u80fd\u975e\u5e38\u4e0d\u786e\u5b9a\u6b64\u65b9\u6cd5\u7684\u5b9e\u9645\u5b9a\u4e49\u4f4d\u7f6e\u3002\u51fa\u4e8e\u8fd9\u4e9b\u539f\u56e0\uff0c\u6211\u5c06\u5728\u672c\u6587\u4e2d\u9650\u5236\u9759\u6001 using \u8bed\u53e5\u7684\u4f7f\u7528\u3002<\/p>\n<p>In any case, at this point in the chapter, you should feel comfortable defining simple class types containing constructors, fields, and various static (and nonstatic) members. Now that you understand the basics of class construction, you can formally investigate the three pillars of object-oriented programming.<br \/>\n\u65e0\u8bba\u5982\u4f55\uff0c\u5728\u672c\u7ae0\u7684\u8fd9\u4e00\u70b9\u4e0a\uff0c\u60a8\u5e94\u8be5\u53ef\u4ee5\u8f7b\u677e\u5730\u5b9a\u4e49\u5305\u542b\u6784\u9020\u51fd\u6570\u3001\u5b57\u6bb5\u548c\u5404\u79cd\u9759\u6001\uff08\u548c\u975e\u9759\u6001\uff09\u6210\u5458\u7684\u7b80\u5355\u7c7b\u7c7b\u578b\u3002\u73b0\u5728\u60a8\u5df2\u7ecf\u4e86\u89e3\u4e86\u7c7b\u6784\u9020\u7684\u57fa\u7840\u77e5\u8bc6\uff0c\u60a8\u53ef\u4ee5\u6b63\u5f0f\u7814\u7a76\u9762\u5411\u5bf9\u8c61\u7f16\u7a0b\u7684\u4e09\u5927\u652f\u67f1\u3002<\/p>\n<h2>Defining the Pillars of OOP<\/h2>\n<p>\u5b9a\u4e49 OOP \u7684\u652f\u67f1<\/p>\n<p>All object-oriented languages (C#, Java, C++, Visual Basic, etc.) must contend with these three core principles, often called the pillars of object-oriented programming (OOP):<br \/>\n\u6240\u6709\u9762\u5411\u5bf9\u8c61\u7684\u8bed\u8a00\uff08C#\uff0cJava\uff0cC++\uff0cVisual Basic\u7b49\uff09\u90fd\u5fc5\u987b\u4e0e\u8fd9\u4e09\u4e2a\u6838\u5fc3\u539f\u5219\u76f8\u6297\u8861\uff0c\u901a\u5e38\u79f0\u4e3a\u9762\u5411\u5bf9\u8c61\u7f16\u7a0b\uff08OOP\uff09\u7684\u652f\u67f1\uff1a<\/p>\n<p>\u2022Encapsulation: How does this language hide an object\u2019s internal implementation details and preserve data integrity?<br \/>\n\u5c01\u88c5\uff1a\u8fd9\u79cd\u8bed\u8a00\u5982\u4f55\u9690\u85cf\u5bf9\u8c61\u7684\u5185\u90e8\u5b9e\u73b0\u7ec6\u8282\u5e76\u4fdd\u6301\u6570\u636e\u5b8c\u6574\u6027\uff1f<br \/>\n\u2022Inheritance: How does this language promote code reuse?<br \/>\n\u7ee7\u627f\uff1a\u8fd9\u79cd\u8bed\u8a00\u5982\u4f55\u4fc3\u8fdb\u4ee3\u7801\u91cd\u7528\uff1f<br \/>\n\u2022Polymorphism: How does this language let you treat related objects in a similar way?<br \/>\n\u591a\u6001\u6027\uff1a\u8fd9\u79cd\u8bed\u8a00\u5982\u4f55\u8ba9\u4f60\u4ee5\u7c7b\u4f3c\u7684\u65b9\u5f0f\u5904\u7406\u76f8\u5173\u5bf9\u8c61\uff1f<\/p>\n<p>Before digging into the details of each pillar, it is important that you understand their basic roles. Here is an overview of each pillar, which will be examined in full detail over the remainder of this chapter and the next.<br \/>\n\u5728\u6df1\u5165\u7814\u7a76\u6bcf\u4e2a\u652f\u67f1\u7684\u7ec6\u8282\u4e4b\u524d\uff0c\u4e86\u89e3\u5b83\u4eec\u7684\u57fa\u672c\u89d2\u8272\u975e\u5e38\u91cd\u8981\u3002\u4ee5\u4e0b\u662f\u6bcf\u4e2a\u652f\u67f1\u7684\u6982\u8ff0\uff0c\u5c06\u5728\u672c\u7ae0\u7684\u5176\u4f59\u90e8\u5206\u548c\u4e0b\u4e00\u7ae0\u4e2d\u5bf9\u5176\u8fdb\u884c\u5168\u9762\u8be6\u7ec6\u7684\u7814\u7a76\u3002<\/p>\n<p>\u25a0 Note  The examples in this section are contained in the oopexamples project of the chapter\u2019s code samples.<br \/>\n\u6ce8\u610f \u672c\u8282\u4e2d\u7684\u793a\u4f8b\u5305\u542b\u5728\u672c\u7ae0\u4ee3\u7801\u793a\u4f8b\u7684 oopexamples \u9879\u76ee\u4e2d\u3002<\/p>\n<h2>Understanding the Role of Encapsulation<\/h2>\n<p>\u4e86\u89e3\u5c01\u88c5\u7684\u4f5c\u7528<\/p>\n<p>The first pillar of OOP is called encapsulation. This trait boils down to the language\u2019s ability to hide unnecessary implementation details from the object user. For example, assume you are using a class named DatabaseReader, which has two primary methods, named Open() and Close().<br \/>\nOOP \u7684\u7b2c\u4e00\u4e2a\u652f\u67f1\u79f0\u4e3a\u5c01\u88c5\u3002\u8fd9\u79cd\u7279\u6027\u5f52\u7ed3\u4e3a\u8bed\u8a00\u80fd\u591f\u5bf9\u5bf9\u8c61\u7528\u6237\u9690\u85cf\u4e0d\u5fc5\u8981\u7684\u5b9e\u73b0\u7ec6\u8282\u3002\u4f8b\u5982\uff0c\u5047\u8bbe\u60a8\u6b63\u5728\u4f7f\u7528\u4e00\u4e2a\u540d\u4e3a DatabaseReader \u7684\u7c7b\uff0c\u8be5\u7c7b\u6709\u4e24\u4e2a\u4e3b\u8981\u65b9\u6cd5\uff0c\u5206\u522b\u540d\u4e3a Open\uff08\uff09 \u548c Close\uff08\uff09\u3002<\/p>\n<p>\/\/ Assume this class encapsulates the details of opening and closing a database.<br \/>\n\u5047\u8bbe\u6b64\u7c7b\u5c01\u88c5\u4e86\u6253\u5f00\u548c\u5173\u95ed\u6570\u636e\u5e93\u7684\u8be6\u7ec6\u4fe1\u606f\u3002<br \/>\nDatabaseReader dbReader = new DatabaseReader(); dbReader.Open(@&quot;C:\\AutoLot.mdf&quot;);<\/p>\n<p>\/\/ Do something with data file and close the file.<br \/>\n\/\/ \u5bf9\u6570\u636e\u6587\u4ef6\u6267\u884c\u67d0\u4e9b\u64cd\u4f5c\u5e76\u5173\u95ed\u8be5\u6587\u4ef6\u3002<br \/>\ndbReader.Close();<\/p>\n<p>The fictitious DatabaseReader class encapsulates the inner details of locating, loading, manipulating, and closing a data file. Programmers love encapsulation, as this pillar of OOP keeps coding tasks simpler. There is no need to worry about the numerous lines of code that are working behind the scenes to carry out the work of the DatabaseReader class. All you do is create an instance and send the appropriate messages (e.g., \u201cOpen the file named AutoLot.mdf located on my C drive\u201d).<br \/>\n\u865a\u6784\u7684 DatabaseReader \u7c7b\u5c01\u88c5\u4e86\u67e5\u627e\u3001\u52a0\u8f7d\u3001\u64cd\u4f5c\u548c\u5173\u95ed\u6570\u636e\u6587\u4ef6\u7684\u5185\u90e8\u8be6\u7ec6\u4fe1\u606f\u3002\u7a0b\u5e8f\u5458\u559c\u6b22\u5c01\u88c5\uff0c\u56e0\u4e3aOOP\u7684\u8fd9\u4e00\u652f\u67f1\u4f7f\u7f16\u7801\u4efb\u52a1\u66f4\u7b80\u5355\u3002\u65e0\u9700\u62c5\u5fc3\u5728\u5e55\u540e\u6267\u884c DatabaseReader \u7c7b\u5de5\u4f5c\u7684\u5927\u91cf\u4ee3\u7801\u884c\u3002\u60a8\u8981\u505a\u7684\u5c31\u662f\u521b\u5efa\u4e00\u4e2a\u5b9e\u4f8b\u5e76\u53d1\u9001\u76f8\u5e94\u7684\u6d88\u606f\uff08\u4f8b\u5982\uff0c\u201c\u6253\u5f00\u4f4d\u4e8e\u6211\u7684 C \u9a71\u52a8\u5668\u4e0a\u7684\u540d\u4e3a AutoLot \u7684\u6587\u4ef6.mdf\u201d\uff09\u3002<\/p>\n<p>Closely related to the notion of encapsulating programming logic is the idea of data protection. Ideally, an object\u2019s state data should be specified using either the private, internal, or protected keyword. In this way, the outside world must ask politely in order to change or obtain the underlying value. This is a good thing, as publicly declared data points can easily become corrupted (ideally by accident rather than intent!). You will formally examine this aspect of encapsulation in just a bit.<br \/>\n\u4e0e\u5c01\u88c5\u7f16\u7a0b\u903b\u8f91\u7684\u6982\u5ff5\u5bc6\u5207\u76f8\u5173\u7684\u662f\u6570\u636e\u4fdd\u62a4\u7684\u6982\u5ff5\u3002\u7406\u60f3\u60c5\u51b5\u4e0b\uff0c\u5e94\u4f7f\u7528\u79c1\u6709\u3001\u5185\u90e8\u6216\u53d7\u4fdd\u62a4\u7684\u5173\u952e\u5b57\u6307\u5b9a\u5bf9\u8c61\u7684\u72b6\u6001\u6570\u636e\u3002\u8fd9\u6837\uff0c\u5916\u754c\u5fc5\u987b\u793c\u8c8c\u5730\u8be2\u95ee\uff0c\u624d\u80fd\u6539\u53d8\u6216\u83b7\u5f97\u6f5c\u5728\u7684\u4ef7\u503c\u3002\u8fd9\u662f\u4e00\u4ef6\u597d\u4e8b\uff0c\u56e0\u4e3a\u516c\u5f00\u58f0\u660e\u7684\u6570\u636e\u70b9\u5f88\u5bb9\u6613\u88ab\u7834\u574f\uff08\u7406\u60f3\u60c5\u51b5\u4e0b\u662f\u5076\u7136\u7684\uff0c\u800c\u4e0d\u662f\u6545\u610f\u7684\uff01\u7a0d\u540e\u60a8\u5c06\u6b63\u5f0f\u68c0\u67e5\u5c01\u88c5\u7684\u8fd9\u4e00\u65b9\u9762\u3002<\/p>\n<h2>Understanding the Role of Inheritance<\/h2>\n<p>\u4e86\u89e3\u7ee7\u627f\u7684\u4f5c\u7528<\/p>\n<p>The next pillar of OOP, inheritance, boils down to the language\u2019s ability to allow you to build new class definitions based on existing class definitions. In essence, inheritance allows you to extend the behavior of a base (or parent) class by inheriting core functionality into the derived subclass (also called a child class). Figure 5-2 shows a simple example.<br \/>\nOOP\u7684\u4e0b\u4e00\u4e2a\u652f\u67f1\uff0c\u7ee7\u627f\uff0c\u5f52\u7ed3\u4e3a\u8bed\u8a00\u7684\u80fd\u529b\uff0c\u5b83\u5141\u8bb8\u4f60\u57fa\u4e8e\u73b0\u6709\u7684\u7c7b\u5b9a\u4e49\u6784\u5efa\u65b0\u7684\u7c7b\u5b9a\u4e49\u3002\u5b9e\u8d28\u4e0a\uff0c\u7ee7\u627f\u5141\u8bb8\u60a8\u901a\u8fc7\u5c06\u6838\u5fc3\u529f\u80fd\u7ee7\u627f\u5230\u6d3e\u751f\u5b50\u7c7b\uff08\u4e5f\u79f0\u4e3a\u5b50\u7c7b\uff09\u4e2d\u6765\u6269\u5c55\u57fa\uff08\u6216\u7236\uff09\u7c7b\u7684\u884c\u4e3a\u3002\u56fe 5-2 \u663e\u793a\u4e86\u4e00\u4e2a\u7b80\u5355\u7684\u793a\u4f8b\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/0502.jpg\" alt=\"Alt text\" \/><\/p>\n<p>Figure 5-2. The \u201cis-a\u201d relationship<br \/>\n\u56fe 5-2\u3002 \u201c\u662f\u201d\u5173\u7cfb<\/p>\n<p>You can read the diagram in Figure 5-2 as \u201cA Hexagon is-a Shape that is-an Object.\u201d When you have classes related by this form of inheritance, you establish \u201cis-a\u201d relationships between types. The \u201cis-a\u201d relationship is termed inheritance.<br \/>\n\u60a8\u53ef\u4ee5\u5c06\u56fe 5-2 \u4e2d\u7684\u56fe\u8868\u89e3\u8bfb\u4e3a\u201c\u516d\u8fb9\u5f62\u662f\u4e00\u4e2a\u5f62\u72b6\uff0c\u5373\u4e00\u4e2a\u5bf9\u8c61\u201d\u3002\u5f53\u60a8\u5177\u6709\u901a\u8fc7\u8fd9\u79cd\u7ee7\u627f\u5f62\u5f0f\u5173\u8054\u7684\u7c7b\u65f6\uff0c\u60a8\u5c06\u5728\u7c7b\u578b\u4e4b\u95f4\u5efa\u7acb\u201cis-a\u201d\u5173\u7cfb\u3002\u201cis-a\u201d\u5173\u7cfb\u79f0\u4e3a\u7ee7\u627f\u3002<\/p>\n<p>Here, you can assume that Shape defines some number of members that are common to all descendants (maybe a value to represent the color to draw the shape and other values to represent the height and width). Given that the Hexagon class extends Shape, it inherits the core functionality defined by Shape and Object, as well as defines additional hexagon-related details of its own (whatever those may be).<br \/>\n\u5728\u8fd9\u91cc\uff0c\u60a8\u53ef\u4ee5\u5047\u8bbe Shape \u5b9a\u4e49\u4e86\u6240\u6709\u540e\u4ee3\u5171\u6709\u7684\u4e00\u4e9b\u6210\u5458\u6570\uff08\u53ef\u80fd\u662f\u4e00\u4e2a\u503c\u6765\u8868\u793a\u7ed8\u5236\u5f62\u72b6\u7684\u989c\u8272\uff0c\u5176\u4ed6\u503c\u6765\u8868\u793a\u9ad8\u5ea6\u548c\u5bbd\u5ea6\uff09\u3002\u9274\u4e8e Hexagon \u7c7b\u6269\u5c55\u4e86 Shape\uff0c\u5b83\u7ee7\u627f\u4e86 Shape \u548c Object \u5b9a\u4e49\u7684\u6838\u5fc3\u529f\u80fd\uff0c\u5e76\u5b9a\u4e49\u4e86\u81ea\u5df1\u7684\u5176\u4ed6\u4e0e\u516d\u8fb9\u5f62\u76f8\u5173\u7684\u7ec6\u8282\uff08\u65e0\u8bba\u8fd9\u4e9b\u7ec6\u8282\u662f\u4ec0\u4e48\uff09\u3002<\/p>\n<p>\u25a0 Note  Under the .neT\/.neT Core platforms, System.Object is always the topmost parent in any class hierarchy, which defines some general functionality for all types (fully described in Chapter 6).<br \/>\n\u6ce8\u610f \u5728.neT\/.neT Core\u5e73\u53f0\u4e0b\uff0cSystem.Object \u59cb\u7ec8\u662f\u4efb\u4f55\u7c7b\u5c42\u6b21\u7ed3\u6784\u4e2d\u6700\u9876\u5c42\u7684\u7236\u7ea7\uff0c\u5b83\u4e3a\u6240\u6709\u7c7b\u578b\u5b9a\u4e49\u4e86\u4e00\u4e9b\u901a\u7528\u529f\u80fd\uff08\u5728\u7b2c 6 \u7ae0\u4e2d\u6709\u5b8c\u6574\u63cf\u8ff0\uff09\u3002<\/p>\n<p>There is another form of code reuse in the world of OOP: the containment\/delegation model also known as the \u201chas-a\u201d relationship or aggregation. This form of reuse is not used to establish parent-child relationships. Rather, the \u201chas-a\u201d relationship allows one class to define a member variable of another class and expose its functionality (if required) to the object user indirectly.<br \/>\n\u5728 OOP \u4e16\u754c\u4e2d\u8fd8\u6709\u53e6\u4e00\u79cd\u5f62\u5f0f\u7684\u4ee3\u7801\u91cd\u7528\uff1a\u5305\u542b\/\u59d4\u6d3e\u6a21\u578b\uff0c\u4e5f\u79f0\u4e3a\u201chas-a\u201d\u5173\u7cfb\u6216\u805a\u5408\u3002\u8fd9\u79cd\u5f62\u5f0f\u7684\u91cd\u7528\u4e0d\u7528\u4e8e\u5efa\u7acb\u7236\u5b50\u5173\u7cfb\u3002\u76f8\u53cd\uff0c\u201chas-a\u201d\u5173\u7cfb\u5141\u8bb8\u4e00\u4e2a\u7c7b\u5b9a\u4e49\u53e6\u4e00\u4e2a\u7c7b\u7684\u6210\u5458\u53d8\u91cf\uff0c\u5e76\u95f4\u63a5\u5411\u5bf9\u8c61\u7528\u6237\u516c\u5f00\u5176\u529f\u80fd\uff08\u5982\u679c\u9700\u8981\uff09\u3002<\/p>\n<p>For example, assume you are again modeling an automobile. You might want to express the idea that a car \u201chas-a\u201d radio. It would be illogical to attempt to derive the Car class from a Radio or vice versa (a Car \u201cis-a\u201d Radio? I think not!). Rather, you have two independent classes working together, where the Car class creates and exposes the Radio\u2019s functionality.<br \/>\n\u4f8b\u5982\uff0c\u5047\u8bbe\u60a8\u518d\u6b21\u5bf9\u6c7d\u8f66\u8fdb\u884c\u5efa\u6a21\u3002\u60a8\u53ef\u80fd\u60f3\u8868\u8fbe\u6c7d\u8f66\u201c\u6709\u201d\u6536\u97f3\u673a\u7684\u60f3\u6cd5\u3002\u8bd5\u56fe\u4ece\u6536\u97f3\u673a\u4e2d\u6d3e\u751f Car \u7c7b\u662f\u4e0d\u5408\u903b\u8f91\u7684\uff0c\u53cd\u4e4b\u4ea6\u7136\uff08\u6c7d\u8f66\u201c\u662f\u201d\u6536\u97f3\u673a\uff1f\u6211\u8ba4\u4e3a\u4e0d\u662f\uff01\u76f8\u53cd\uff0c\u60a8\u6709\u4e24\u4e2a\u72ec\u7acb\u7684\u7c7b\u4e00\u8d77\u5de5\u4f5c\uff0c\u5176\u4e2d Car \u7c7b\u521b\u5efa\u5e76\u516c\u5f00\u65e0\u7ebf\u7535\u7684\u529f\u80fd\u3002<\/p>\n<p>namespace OopExamples; class Radio<br \/>\n{<br \/>\npublic void Power(bool turnOn)<br \/>\n{<br \/>\nConsole.WriteLine(&quot;Radio on: {0}&quot;, turnOn);<br \/>\n}<br \/>\n}<\/p>\n<p>namespace OopExamples; class Car<br \/>\n{<br \/>\n\/\/ Car 'has-a' Radio.<br \/>\nprivate Radio myRadio = new Radio(); public void TurnOnRadio(bool onOff)<br \/>\n{<br \/>\n\/\/ Delegate call to inner object. myRadio.Power(onOff);<br \/>\n}<br \/>\n}<\/p>\n<p>Notice that the object user has no clue that the Car class is using an inner Radio object.<br \/>\n\u8bf7\u6ce8\u610f\uff0c\u5bf9\u8c61\u7528\u6237\u4e0d\u77e5\u9053 Car \u7c7b\u6b63\u5728\u4f7f\u7528\u5185\u90e8 Radio \u5bf9\u8c61\u3002<\/p>\n<p>using OopExamples;<\/p>\n<p>Console.WriteLine(&quot;--- Fun with OOP examples ---&quot;);<\/p>\n<p>\/\/ Call is forwarded to Radio internally. Car viper = new Car(); viper.TurnOnRadio(false);<\/p>\n<h2>Understanding the Role of Polymorphism<\/h2>\n<p>\u4e86\u89e3\u591a\u6001\u6027\u7684\u4f5c\u7528<\/p>\n<p>The final pillar of OOP is polymorphism. This trait captures a language\u2019s ability to treat related objects in a similar manner. Specifically, this tenant of an object-oriented language allows a base class to define aset of members (formally termed the polymorphic interface) that are available to all descendants. A class\u2019s polymorphic interface is constructed using any number of virtual or abstract members (see Chapter 6 for full details).<br \/>\nOOP\u7684\u6700\u540e\u4e00\u4e2a\u652f\u67f1\u662f\u591a\u6001\u6027\u3002\u6b64\u7279\u5f81\u6355\u83b7\u4e86\u8bed\u8a00\u4ee5\u7c7b\u4f3c\u65b9\u5f0f\u5904\u7406\u76f8\u5173\u5bf9\u8c61\u7684\u80fd\u529b\u3002\u5177\u4f53\u6765\u8bf4\uff0c\u9762\u5411\u5bf9\u8c61\u8bed\u8a00\u7684\u8fd9\u4e2a\u79df\u6237\u5141\u8bb8\u57fa\u7c7b\u5b9a\u4e49\u53ef\u4f9b\u6240\u6709\u540e\u4ee3\u4f7f\u7528\u7684\u6210\u5458\u96c6\uff08\u6b63\u5f0f\u79f0\u4e3a\u591a\u6001\u63a5\u53e3\uff09\u3002\u7c7b\u7684\u591a\u6001\u63a5\u53e3\u662f\u4f7f\u7528\u4efb\u610f\u6570\u91cf\u7684\u865a\u62df\u6216\u62bd\u8c61\u6210\u5458\u6784\u9020\u7684\uff08\u6709\u5173\u5b8c\u6574\u8be6\u7ec6\u4fe1\u606f\uff0c\u8bf7\u53c2\u9605\u7b2c 6 \u7ae0\uff09\u3002<\/p>\n<p>In a nutshell, a virtual member is a member in a base class that defines a default implementation that may be changed (or more formally speaking, overridden) by a derived class. In contrast, an abstract method is a member in a base class that does not provide a default implementation but does provide a signature.<br \/>\nWhen a class derives from a base class defining an abstract method, it must be overridden by a derived type. In either case, when derived types override the members defined by a base class, they are essentially redefining how they respond to the same request.<br \/>\nOOP\u7684\u6700\u540e\u4e00\u4e2a\u652f\u67f1\u662f\u591a\u6001\u6027\u3002\u6b64\u7279\u5f81\u6355\u83b7\u4e86\u8bed\u8a00\u4ee5\u7c7b\u4f3c\u65b9\u5f0f\u5904\u7406\u76f8\u5173\u5bf9\u8c61\u7684\u80fd\u529b\u3002\u5177\u4f53\u6765\u8bf4\uff0c\u9762\u5411\u5bf9\u8c61\u8bed\u8a00\u7684\u8fd9\u4e2a\u79df\u6237\u5141\u8bb8\u57fa\u7c7b\u5b9a\u4e49\u53ef\u4f9b\u6240\u6709\u540e\u4ee3\u4f7f\u7528\u7684\u6210\u5458\u96c6\uff08\u6b63\u5f0f\u79f0\u4e3a\u591a\u6001\u63a5\u53e3\uff09\u3002\u7c7b\u7684\u591a\u6001\u63a5\u53e3\u662f\u4f7f\u7528\u4efb\u610f\u6570\u91cf\u7684\u865a\u62df\u6216\u62bd\u8c61\u6210\u5458\u6784\u9020\u7684\uff08\u6709\u5173\u5b8c\u6574\u8be6\u7ec6\u4fe1\u606f\uff0c\u8bf7\u53c2\u9605\u7b2c 6 \u7ae0\uff09\u3002<\/p>\n<p>To preview polymorphism, let\u2019s provide some details behind the shapes hierarchy shown in Figure 5-3.<br \/>\n\u4e3a\u4e86\u9884\u89c8\u591a\u6001\u6027\uff0c\u8ba9\u6211\u4eec\u63d0\u4f9b\u56fe 5-3 \u4e2d\u6240\u793a\u7684\u5f62\u72b6\u5c42\u6b21\u7ed3\u6784\u80cc\u540e\u7684\u4e00\u4e9b\u8be6\u7ec6\u4fe1\u606f\u3002<\/p>\n<p>Assume that the Shape class has defined a virtual method named Draw() that takes no parameters. Given that every shape needs to render itself in a unique manner, subclasses such as Hexagon and Circle are free to override this method to their own liking (see Figure 5-3).<br \/>\n\u5047\u8bbe Shape \u7c7b\u5b9a\u4e49\u4e86\u4e00\u4e2a\u540d\u4e3a Draw\uff08\uff09 \u7684\u865a\u62df\u65b9\u6cd5\uff0c\u8be5\u65b9\u6cd5\u4e0d\u5e26\u4efb\u4f55\u53c2\u6570\u3002\u9274\u4e8e\u6bcf\u4e2a\u5f62\u72b6\u90fd\u9700\u8981\u4ee5\u72ec\u7279\u7684\u65b9\u5f0f\u5448\u73b0\u81ea\u8eab\uff0cHexagon \u548c Circle \u7b49\u5b50\u7c7b\u53ef\u4ee5\u6839\u636e\u81ea\u5df1\u7684\u559c\u597d\u81ea\u7531\u8986\u76d6\u6b64\u65b9\u6cd5\uff08\u53c2\u89c1\u56fe 5-3\uff09\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/0503.jpg\" alt=\"Alt text\" \/><\/p>\n<p>Figure 5-3. Classical polymorphism<br \/>\n\u56fe 5-3\u3002 \u7ecf\u5178\u591a\u6001\u6027<\/p>\n<p>After a polymorphic interface has been designed, you can begin to make various assumptions in your code. For example, given that Hexagon and Circle derive from a common parent (Shape), an array of Shape types could contain anything deriving from this base class. Furthermore, given that Shape defines<br \/>\na polymorphic interface to all derived types (the Draw() method in this example), you can assume each member in the array has this functionality.<br \/>\n\u8bbe\u8ba1\u591a\u6001\u63a5\u53e3\u540e\uff0c\u53ef\u4ee5\u5f00\u59cb\u5728\u4ee3\u7801\u4e2d\u505a\u51fa\u5404\u79cd\u5047\u8bbe\u3002\u4f8b\u5982\uff0c\u5047\u8bbe Hexagon \u548c Circle \u6d3e\u751f\u81ea\u516c\u5171\u7236\u7ea7 \uff08Shape\uff09\uff0c\u5219 Shape \u7c7b\u578b\u7684\u6570\u7ec4\u53ef\u4ee5\u5305\u542b\u4ece\u6b64\u57fa\u7c7b\u6d3e\u751f\u7684\u4efb\u4f55\u5185\u5bb9\u3002\u6b64\u5916\uff0c\u9274\u4e8e\u5f62\u72b6\u5b9a\u4e49\u6240\u6709\u6d3e\u751f\u7c7b\u578b\u7684\u591a\u6001\u63a5\u53e3\uff08\u6b64\u793a\u4f8b\u4e2d\u7684 Draw\uff08\uff09 \u65b9\u6cd5\uff09\uff0c\u53ef\u4ee5\u5047\u5b9a\u6570\u7ec4\u4e2d\u7684\u6bcf\u4e2a\u6210\u5458\u90fd\u5177\u6709\u6b64\u529f\u80fd\u3002<\/p>\n<p>Consider the following code, which instructs an array of Shape-derived types to render themselves using the Draw() method:<br \/>\n\u8bf7\u8003\u8651\u4ee5\u4e0b\u4ee3\u7801\uff0c\u8be5\u4ee3\u7801\u6307\u793a Shape \u6d3e\u751f\u7c7b\u578b\u7684\u6570\u7ec4\u4f7f\u7528 Draw\uff08\uff09 \u65b9\u6cd5\u5448\u73b0\u81ea\u8eab\uff1a<\/p>\n<p>Shape[] myShapes = new Shape[3]; myShapes[0] = new Hexagon(); myShapes[1] = new Circle(); myShapes[2] = new Hexagon();<\/p>\n<p>foreach (Shape s in myShapes)<br \/>\n{<br \/>\n\/\/ Use the polymorphic interface! s.Draw();<br \/>\n}<br \/>\nConsole.ReadLine();<\/p>\n<p>This wraps up our brisk overview of the pillars of OOP. Now that you have the theory in your mind, the remainder of this chapter explores further details of how encapsulation is handled under C#, starting with a look at access modifiers. Chapter 6 will tackle the details of inheritance and polymorphism.<br \/>\n\u4ee5\u4e0a\u603b\u7ed3\u4e86\u6211\u4eec\u5bf9 OOP \u652f\u67f1\u7684\u5feb\u901f\u6982\u8ff0\u3002\u73b0\u5728\u60a8\u5df2\u7ecf\u638c\u63e1\u4e86\u8be5\u7406\u8bba\uff0c\u672c\u7ae0\u7684\u5176\u4f59\u90e8\u5206\u5c06\u63a2\u8ba8\u5982\u4f55\u5728 C# \u4e0b\u5904\u7406\u5c01\u88c5\u7684\u66f4\u591a\u8be6\u7ec6\u4fe1\u606f\uff0c\u9996\u5148\u4ecb\u7ecd\u8bbf\u95ee\u4fee\u9970\u7b26\u3002\u7b2c6\u7ae0\u5c06\u8ba8\u8bba\u9057\u4f20\u548c\u591a\u6001\u6027\u7684\u7ec6\u8282\u3002<\/p>\n<h2>Understanding C# Access Modifiers (Updated 7.2)<\/h2>\n<p>\u4e86\u89e3 C# \u8bbf\u95ee\u4fee\u9970\u7b26\uff087.2 \u66f4\u65b0\uff09<\/p>\n<p>When working with encapsulation, you must always consider which aspects of a type are visible to various parts of your application. Specifically, types (classes, interfaces, structures, enumerations, and delegates) as well as their members (properties, methods, constructors, and fields) are defined using a specific keyword to control how \u201cvisible\u201d the item is to other parts of your application. Although C# defines numerous keywords to control access, they differ on where they can be successfully applied (type or member). Table 5-1 documents the role of each access modifier and where it may be applied.<br \/>\n\u4f7f\u7528\u5c01\u88c5\u65f6\uff0c\u5fc5\u987b\u59cb\u7ec8\u8003\u8651\u7c7b\u578b\u7684\u54ea\u4e9b\u65b9\u9762\u5bf9\u5e94\u7528\u7a0b\u5e8f\u7684\u5404\u4e2a\u90e8\u5206\u53ef\u89c1\u3002\u5177\u4f53\u800c\u8a00\uff0c\u7c7b\u578b\uff08\u7c7b\u3001\u63a5\u53e3\u3001\u7ed3\u6784\u3001\u679a\u4e3e\u548c\u59d4\u6258\uff09\u53ca\u5176\u6210\u5458\uff08\u5c5e\u6027\u3001\u65b9\u6cd5\u3001\u6784\u9020\u51fd\u6570\u548c\u5b57\u6bb5\uff09\u662f\u4f7f\u7528\u7279\u5b9a\u5173\u952e\u5b57\u5b9a\u4e49\u7684\uff0c\u4ee5\u63a7\u5236\u9879\u5bf9\u5e94\u7528\u7a0b\u5e8f\u5176\u4ed6\u90e8\u5206\u7684\u201c\u53ef\u89c1\u201d\u7a0b\u5ea6\u3002\u5c3d\u7ba1 C# \u5b9a\u4e49\u4e86\u8bb8\u591a\u5173\u952e\u5b57,\u4e3a\u4e86\u63a7\u5236\u8bbf\u95ee\uff0c\u5b83\u4eec\u5728\u6210\u529f\u5e94\u7528\u7684\u4f4d\u7f6e\uff08\u7c7b\u578b\u6216\u6210\u5458\uff09\u4e0a\u6709\u6240\u4e0d\u540c\u3002\u8868 5-1 \u8bb0\u5f55\u4e86\u6bcf\u4e2a\u8bbf\u95ee\u4fee\u9970\u7b26\u7684\u89d2\u8272\u53ca\u5176\u5e94\u7528\u4f4d\u7f6e\u3002<\/p>\n<p>Table 5-1. C# Access Modifiers<br \/>\n\u8868 5-1. C# \u8bbf\u95ee\u4fee\u9970\u7b26<\/p>\n<table>\n<tr>\n<td>C# Access Modifier<br \/>\n        C# \u8bbf\u95ee\u4fee\u9970\u7b26<\/td>\n<td>May Be Applied To<br \/>\n        \u53ef\u5e94\u7528\u4e8e<\/td>\n<td>Meaning in Life<br \/>\n        \u610f\u4e49<\/td>\n<\/tr>\n<tr>\n<td>public<\/td>\n<td>Types or type members<br \/>\n        \u7c7b\u578b\u6216\u7c7b\u578b\u6210\u5458<\/td>\n<td>Public items have no access restrictions. A public member can be accessed from an object, as well as any derived class. A public type can be accessed from other external assemblies.<br \/>\n        \u516c\u5171\u9879\u76ee\u6ca1\u6709\u8bbf\u95ee\u9650\u5236\u3002\u53ef\u4ee5\u4ece\u5bf9\u8c61\u4ee5\u53ca\u4efb\u4f55\u6d3e\u751f\u7c7b\u8bbf\u95ee\u516c\u5171\u6210\u5458\u3002\u53ef\u4ee5\u4ece\u5176\u4ed6\u5916\u90e8\u7a0b\u5e8f\u96c6\u8bbf\u95ee\u516c\u5171\u7c7b\u578b\u3002<\/td>\n<\/tr>\n<tr>\n<td>private<\/td>\n<td>Type members or nested types<br \/>\n        \u7c7b\u578b\u6210\u5458\u6216\u5d4c\u5957\u7c7b\u578b<\/td>\n<td>Private items can be accessed only by the class (or structure) that defines the item.<br \/>\n        \u79c1\u6709\u9879\u53ea\u80fd\u7531\u5b9a\u4e49\u8be5\u9879\u7684\u7c7b\uff08\u6216\u7ed3\u6784\uff09\u8bbf\u95ee\u3002<\/td>\n<\/tr>\n<tr>\n<td>protected<\/td>\n<td>Type members or nested types<br \/>\n        \u7c7b\u578b\u6210\u5458\u6216\u5d4c\u5957\u7c7b\u578b<\/td>\n<td>Protected items can be used by the class that defines it and any child class. They cannot be accessed from outside the inheritance chain.<br \/>\n        \u53d7\u4fdd\u62a4\u9879\u53ef\u7531\u5b9a\u4e49\u5b83\u7684\u7c7b\u548c\u4efb\u4f55\u5b50\u7c7b\u4f7f\u7528\u3002\u4e0d\u80fd\u4ece\u7ee7\u627f\u94fe\u5916\u90e8\u8bbf\u95ee\u5b83\u4eec\u3002<\/td>\n<\/tr>\n<tr>\n<td>internal<\/td>\n<td>Types or type members<br \/>\n        \u7c7b\u578b\u6216\u7c7b\u578b\u6210\u5458<\/td>\n<td>Internal items are accessible only within the current assembly. Other assemblies can be explicitly granted permission to see the internal items.<br \/>\n        \u5185\u90e8\u9879\u53ea\u80fd\u5728\u5f53\u524d\u7a0b\u5e8f\u96c6\u5185\u8bbf\u95ee\u3002\u53ef\u4ee5\u663e\u5f0f\u6388\u4e88\u5176\u4ed6\u7a0b\u5e8f\u96c6\u67e5\u770b\u5185\u90e8\u9879\u7684\u6743\u9650\u3002<\/td>\n<\/tr>\n<tr>\n<td>protected internal<\/td>\n<td>Type members or nested types<br \/>\n        \u7c7b\u578b\u6210\u5458\u6216\u5d4c\u5957\u7c7b\u578b<\/td>\n<td>When the protected and internal keywords are combined on an item, the item is accessible within the defining assembly, within the defining class, and by derived classes inside or outside of the defining assembly.<br \/>\n        \u5728\u9879\u4e0a\u7ec4\u5408\u53d7\u4fdd\u62a4\u5173\u952e\u5b57\u548c\u5185\u90e8\u5173\u952e\u5b57\u65f6\uff0c\u53ef\u4ee5\u5728\u5b9a\u4e49\u7a0b\u5e8f\u96c6\u5185\u3001\u5b9a\u4e49\u7c7b\u5185\u4ee5\u53ca\u5b9a\u4e49\u7a0b\u5e8f\u96c6\u5185\u90e8\u6216\u5916\u90e8\u7684\u6d3e\u751f\u7c7b\u8bbf\u95ee\u8be5\u9879\u3002<\/td>\n<\/tr>\n<tr>\n<td>private protected (new 7.2)<\/td>\n<td>Type members or nested types<br \/>\n        \u7c7b\u578b\u6210\u5458\u6216\u5d4c\u5957\u7c7b\u578b<\/td>\n<td>When the private and protected keywords are combined on an item, the item is accessible within the defining class and by derived classes in the same assembly.<br \/>\n        \u5728\u9879\u4e0a\u7ec4\u5408\u79c1\u6709\u5173\u952e\u5b57\u548c\u53d7\u4fdd\u62a4\u5173\u952e\u5b57\u65f6\uff0c\u53ef\u4ee5\u5728\u5b9a\u4e49\u7c7b\u4e2d\u8bbf\u95ee\u8be5\u9879\uff0c\u4e5f\u53ef\u4ee5\u7531\u540c\u4e00\u7a0b\u5e8f\u96c6\u4e2d\u7684\u6d3e\u751f\u7c7b\u8bbf\u95ee\u8be5\u9879\u3002<\/td>\n<\/tr>\n<\/table>\n<p>In this chapter, you are concerned only with the public and private keywords. Later chapters will examine the role of the internal and protected internal modifiers (useful when you build code libraries and unit tests) and the protected modifier (useful when you are creating class hierarchies).<br \/>\n\u5728\u672c\u7ae0\u4e2d\uff0c\u60a8\u53ea\u5173\u6ce8\u516c\u5171\u548c\u79c1\u6709\u5173\u952e\u5b57\u3002\u540e\u9762\u7684\u7ae0\u8282\u5c06\u7814\u7a76\u5185\u90e8\u548c\u53d7\u4fdd\u62a4\u7684\u5185\u90e8\u4fee\u9970\u7b26\uff08\u5728\u6784\u5efa\u4ee3\u7801\u5e93\u548c\u5355\u5143\u6d4b\u8bd5\u65f6\u5f88\u6709\u7528\uff09\u548c\u53d7\u4fdd\u62a4\u4fee\u9970\u7b26\uff08\u5728\u521b\u5efa\u7c7b\u5c42\u6b21\u7ed3\u6784\u65f6\u5f88\u6709\u7528\uff09\u7684\u4f5c\u7528\u3002<\/p>\n<h1>Using the Default Access Modifiers<\/h1>\n<p>\u4f7f\u7528\u9ed8\u8ba4\u8bbf\u95ee\u4fee\u9970\u7b26<\/p>\n<p>By default, type members are implicitly private, while types are implicitly internal. Thus, the following class definition is automatically set to internal, while the type\u2019s default constructor is automatically set to private (however, as you would suspect, there are few times you would want a private class constructor):<br \/>\n\u9ed8\u8ba4\u60c5\u51b5\u4e0b\uff0c\u7c7b\u578b\u6210\u5458\u662f\u9690\u5f0f\u79c1\u6709\u7684\uff0c\u800c\u7c7b\u578b\u662f\u9690\u5f0f\u5185\u90e8\u7684\u3002\u56e0\u6b64\uff0c\u4ee5\u4e0b\u7c7b\u5b9a\u4e49\u81ea\u52a8\u8bbe\u7f6e\u4e3a\u5185\u90e8\uff0c\u800c\u7c7b\u578b\u7684\u9ed8\u8ba4\u6784\u9020\u51fd\u6570\u81ea\u52a8\u8bbe\u7f6e\u4e3a private\uff08\u4f46\u662f\uff0c\u6b63\u5982\u60a8\u6240\u6000\u7591\u7684\u90a3\u6837\uff0c\u5f88\u5c11\u9700\u8981\u79c1\u6709\u7c7b\u6784\u9020\u51fd\u6570\uff09\uff1a<\/p>\n<p>\/\/ An internal class with a private default constructor. class Radio<br \/>\n{<br \/>\nRadio(){}<br \/>\n}<\/p>\n<p>If you want to be explicit, you could add these keywords yourself with no ill effect (beyond a few additional keystrokes).<br \/>\n\u5982\u679c\u4f60\u60f3\u660e\u786e\u4e00\u70b9\uff0c\u4f60\u53ef\u4ee5\u81ea\u5df1\u6dfb\u52a0\u8fd9\u4e9b\u5173\u952e\u5b57\uff0c\u800c\u4e0d\u4f1a\u4ea7\u751f\u4efb\u4f55\u4e0d\u826f\u5f71\u54cd\uff08\u9664\u4e86\u51e0\u4e2a\u989d\u5916\u7684\u51fb\u952e\uff09\u3002<\/p>\n<p>\/\/ An internal class with a private default constructor. internal class Radio<br \/>\n{<br \/>\nprivate Radio(){}<br \/>\n}<\/p>\n<p>To allow other parts of a program to invoke members of an object, you must define them with the public keyword (or possibly with the protected keyword, which you will learn about in the next chapter). As well, if you want to expose the Radio to external assemblies (again, useful when building larger solutions or code libraries), you will need to add the public modifier.<br \/>\n\u8981\u5141\u8bb8\u7a0b\u5e8f\u7684\u5176\u4ed6\u90e8\u5206\u8c03\u7528\u5bf9\u8c61\u7684\u6210\u5458\uff0c\u60a8\u5fc5\u987b\u4f7f\u7528 public \u5173\u952e\u5b57\uff08\u6216\u8005\u53ef\u80fd\u4f7f\u7528 protected \u5173\u952e\u5b57\uff0c\u60a8\u5c06\u5728\u4e0b\u4e00\u7ae0\u4e2d\u4e86\u89e3\uff09\u5b9a\u4e49\u5b83\u4eec\u3002\u6b64\u5916\uff0c\u5982\u679c\u8981\u5411\u5916\u90e8\u7a0b\u5e8f\u96c6\u516c\u5f00 Radio\uff08\u540c\u6837\uff0c\u5728\u751f\u6210\u8f83\u5927\u7684\u89e3\u51b3\u65b9\u6848\u6216\u4ee3\u7801\u5e93\u65f6\u5f88\u6709\u7528\uff09\uff0c\u5219\u9700\u8981\u6dfb\u52a0 public \u4fee\u9970\u7b26\u3002<\/p>\n<p>\/\/ A public class with a public default constructor. public class Radio<br \/>\n{<br \/>\npublic Radio(){}<br \/>\n}<\/p>\n<h2>Using Access Modifiers and Nested Types<\/h2>\n<p>\u4f7f\u7528\u8bbf\u95ee\u4fee\u9970\u7b26\u548c\u5d4c\u5957\u7c7b\u578b<\/p>\n<p>As mentioned in Table 5-1, the private, protected, protected internal, and private protected access modifiers can be applied to a nested type. Chapter 6 will examine nesting in detail. What you need to know at this point, however, is that a nested type is a type declared directly within the scope of class or structure. By way of example, here is a private enumeration (named CarColor) nested within a public class (named SportsCar):<br \/>\n\u5982\u8868 5-1 \u4e2d\u6240\u8ff0\uff0c\u79c1\u6709\u3001\u53d7\u4fdd\u62a4\u3001\u53d7\u4fdd\u62a4\u7684\u5185\u90e8\u548c\u79c1\u6709\u53d7\u4fdd\u62a4\u7684\u8bbf\u95ee\u4fee\u9970\u7b26\u53ef\u4ee5\u5e94\u7528\u4e8e\u5d4c\u5957\u7c7b\u578b\u3002\u7b2c6\u7ae0\u5c06\u8be6\u7ec6\u7814\u7a76\u5d4c\u5957\u3002\u4f46\u662f\uff0c\u6b64\u65f6\u60a8\u9700\u8981\u77e5\u9053\u7684\u662f\uff0c\u5d4c\u5957\u7c7b\u578b\u662f\u76f4\u63a5\u5728\u7c7b\u6216\u7ed3\u6784\u8303\u56f4\u5185\u58f0\u660e\u7684\u7c7b\u578b\u3002\u4f8b\u5982\uff0c\u4e0b\u9762\u662f\u5d4c\u5957\u5728\u516c\u5171\u7c7b\uff08\u540d\u4e3a SportsCar\uff09\u4e2d\u7684\u79c1\u6709\u679a\u4e3e\uff08\u540d\u4e3a CarColor\uff09\uff1a<\/p>\n<p>namespace OopExamples; public class SportsCar<br \/>\n{<br \/>\n\/\/ OK! Nested types can be marked private. private enum CarColor<br \/>\n{<br \/>\nRed, Green, Blue<br \/>\n}<br \/>\n}<\/p>\n<p>Here, it is permissible to apply the private access modifier on the nested type. However, non-nested types (such as the SportsCar) can be defined only with the public or internal modifier. Therefore, the following class definition is illegal:<br \/>\n\u5728\u8fd9\u91cc\uff0c\u5141\u8bb8\u5728\u5d4c\u5957\u7c7b\u578b\u4e0a\u5e94\u7528\u4e13\u7528\u8bbf\u95ee\u4fee\u9970\u7b26\u3002\u4f46\u662f\uff0c\u975e\u5d4c\u5957\u7c7b\u578b\uff08\u5982 SportsCar\uff09\u53ea\u80fd\u4f7f\u7528\u516c\u5171\u6216\u5185\u90e8\u4fee\u9970\u7b26\u8fdb\u884c\u5b9a\u4e49\u3002\u56e0\u6b64\uff0c\u4ee5\u4e0b\u7c7b\u5b9a\u4e49\u662f\u975e\u6cd5\u7684\uff1a<\/p>\n<p>\/\/ Error! Non-nested types cannot be marked private! private class SportsCar<br \/>\n{}<\/p>\n<h2>Understanding the First Pillar: C#\u2019s Encapsulation Services<\/h2>\n<p>\u4e86\u89e3\u7b2c\u4e00\u4e2a\u652f\u67f1\uff1aC# \u7684\u5c01\u88c5\u670d\u52a1<\/p>\n<p>The concept of encapsulation revolves around the notion that an object\u2019s data should not be directly accessible from an object instance. Rather, class data is defined as private. If the object user wants to alter the state of an object, it does so indirectly using public members. To illustrate the need for encapsulation services, assume you have created the following class definition:<br \/>\n\u5c01\u88c5\u7684\u6982\u5ff5\u56f4\u7ed5\u7740\u8fd9\u6837\u4e00\u4e2a\u6982\u5ff5\uff0c\u5373\u4e0d\u5e94\u4ece\u5bf9\u8c61\u5b9e\u4f8b\u76f4\u63a5\u8bbf\u95ee\u5bf9\u8c61\u7684\u6570\u636e\u3002\u76f8\u53cd\uff0c\u7c7b\u6570\u636e\u88ab\u5b9a\u4e49\u4e3a\u79c1\u6709\u7684\u3002\u5982\u679c\u5bf9\u8c61\u7528\u6237\u60f3\u8981\u66f4\u6539\u5bf9\u8c61\u7684\u72b6\u6001\uff0c\u5b83\u5c06\u4f7f\u7528\u516c\u5171\u6210\u5458\u95f4\u63a5\u6267\u884c\u6b64\u64cd\u4f5c\u3002\u4e3a\u4e86\u8bf4\u660e\u5bf9\u5c01\u88c5\u670d\u52a1\u7684\u9700\u6c42\uff0c\u5047\u8bbe\u60a8\u5df2\u7ecf\u521b\u5efa\u4e86\u4ee5\u4e0b\u7c7b\u5b9a\u4e49\uff1a<\/p>\n<p>\/\/ A class with a single public field. class Book<br \/>\n{<br \/>\npublic int numberOfPages;<br \/>\n}<\/p>\n<p>The problem with public data is that the data itself has no ability to \u201cunderstand\u201d whether the current value to which it is assigned is valid with regard to the current business rules of the system. As you know, the upper range of a C# int is quite large (2,147,483,647). Therefore, the compiler allows the following assignment:<br \/>\n\u516c\u5171\u6570\u636e\u7684\u95ee\u9898\u5728\u4e8e\uff0c\u6570\u636e\u672c\u8eab\u65e0\u6cd5\u201c\u7406\u89e3\u201d\u5b83\u6240\u8d4b\u503c\u7684\u5f53\u524d\u503c\u5bf9\u4e8e\u7cfb\u7edf\u7684\u5f53\u524d\u4e1a\u52a1\u89c4\u5219\u662f\u5426\u6709\u6548\u3002\u5982\u60a8\u6240\u77e5\uff0cC# int \u7684\u4e0a\u9650\u975e\u5e38\u5927 \uff082,147,483,647\uff09\u3002\u56e0\u6b64\uff0c\u7f16\u8bd1\u5668\u5141\u8bb8\u4ee5\u4e0b\u8d4b\u503c\uff1a<\/p>\n<p>\/\/ Humm. That is one heck of a mini-novel! Book miniNovel = new Book(); miniNovel.numberOfPages = 30_000_000;<\/p>\n<p>Although you have not overflowed the boundaries of an int data type, it should be clear that a mini- novel with a page count of 30,000,000 pages is a bit unreasonable. As you can see, public fields do not provide a way to trap logical upper (or lower) limits. If your current system has a business rule that states a book must be between 1 and 1,000 pages, you are at a loss to enforce this programmatically. Because of this, public fields typically have no place in a production-level class definition.<br \/>\n\u867d\u7136\u4f60\u6ca1\u6709\u6ea2\u51fa int \u6570\u636e\u7c7b\u578b\u7684\u754c\u9650\uff0c\u4f46\u5e94\u8be5\u6e05\u695a\u7684\u662f\uff0c\u9875\u6570\u4e3a 30,000,000 \u9875\u7684\u8ff7\u4f60\u5c0f\u8bf4\u6709\u70b9\u4e0d\u5408\u7406\u3002\u5982\u60a8\u6240\u89c1\uff0c\u516c\u5171\u5b57\u6bb5\u4e0d\u63d0\u4f9b\u6355\u83b7\u903b\u8f91\u4e0a\u9650\uff08\u6216\u4e0b\u9650\uff09\u7684\u65b9\u6cd5\u3002\u5982\u679c\u60a8\u5f53\u524d\u7684\u7cfb\u7edf\u5177\u6709\u89c4\u5b9a\u4e66\u7c4d\u5fc5\u987b\u4ecb\u4e8e 1 \u5230 1,000 \u9875\u4e4b\u95f4\u7684\u4e1a\u52a1\u89c4\u5219\uff0c\u5219\u60a8\u65e0\u6cd5\u4ee5\u7f16\u7a0b\u65b9\u5f0f\u5f3a\u5236\u6267\u884c\u6b64\u89c4\u5219\u3002\u56e0\u6b64\uff0c\u516c\u5171\u5b57\u6bb5\u5728\u751f\u4ea7\u7ea7\u7c7b\u5b9a\u4e49\u4e2d\u901a\u5e38\u6ca1\u6709\u4f4d\u7f6e\u3002<\/p>\n<p>\u25a0 Note  To be more specific, members of a class that represent an object\u2019s state should not be marked as public. as you will see later in this chapter, public constants and public read-only fields are quite useful.<br \/>\n\u6ce8\u610f \u66f4\u5177\u4f53\u5730\u8bf4\uff0c\u8868\u793a\u5bf9\u8c61\u72b6\u6001\u7684\u7c7b\u7684\u6210\u5458\u4e0d\u5e94\u6807\u8bb0\u4e3a\u516c\u5171\u3002 \u6b63\u5982\u60a8\u5c06\u5728\u672c\u7ae0\u540e\u9762\u770b\u5230\u7684\uff0c\u516c\u5171\u5e38\u91cf\u548c\u516c\u5171\u53ea\u8bfb\u5b57\u6bb5\u975e\u5e38\u6709\u7528\u3002<\/p>\n<p>Encapsulation provides a way to preserve the integrity of an object\u2019s state data. Rather than defining public fields (which can easily foster data corruption), you should get in the habit of defining private data, which is indirectly manipulated using one of two main techniques.<br \/>\n\u5c01\u88c5\u63d0\u4f9b\u4e86\u4e00\u79cd\u4fdd\u6301\u5bf9\u8c61\u72b6\u6001\u6570\u636e\u5b8c\u6574\u6027\u7684\u65b9\u6cd5\u3002\u4e0e\u5176\u5b9a\u4e49\u516c\u5171\u5b57\u6bb5\uff08\u8fd9\u5f88\u5bb9\u6613\u52a9\u957f\u6570\u636e\u635f\u574f\uff09\uff0c\u4e0d\u5982\u517b\u6210\u5b9a\u4e49\u79c1\u6709\u6570\u636e\u7684\u4e60\u60ef\uff0c\u8fd9\u662f\u4f7f\u7528\u4e24\u79cd\u4e3b\u8981\u6280\u672f\u4e4b\u4e00\u95f4\u63a5\u64cd\u4f5c\u7684\u3002<\/p>\n<p>\u2022You can define a pair of public accessor (get) and mutator (set) methods.<br \/>\n\u60a8\u53ef\u4ee5\u5b9a\u4e49\u4e00\u5bf9\u516c\u5171\u8bbf\u95ee\u5668 \uff08get\uff09 \u548c\u7a81\u53d8\u5668 \uff08set\uff09 \u65b9\u6cd5\u3002<br \/>\n\u2022You can define a public property.<br \/>\n\u60a8\u53ef\u4ee5\u5b9a\u4e49\u516c\u5171\u5c5e\u6027\u3002<\/p>\n<p>Whichever technique you choose, the point is that a well-encapsulated class should protect its data and hide the details of how it operates from the prying eyes of the outside world. This is often termed black- box programming. The beauty of this approach is that an object is free to change how a given method is implemented under the hood. It does this without breaking any existing code making use of it, provided that the parameters and return values of the method remain constant.<br \/>\n\u65e0\u8bba\u60a8\u9009\u62e9\u54ea\u79cd\u6280\u672f\uff0c\u5173\u952e\u662f\u5c01\u88c5\u826f\u597d\u7684\u7c7b\u5e94\u8be5\u4fdd\u62a4\u5176\u6570\u636e\uff0c\u5e76\u9690\u85cf\u5176\u64cd\u4f5c\u65b9\u5f0f\u7684\u7ec6\u8282\uff0c\u4ee5\u514d\u88ab\u5916\u754c\u7aa5\u63a2\u3002\u8fd9\u901a\u5e38\u88ab\u79f0\u4e3a\u9ed1\u76d2\u7f16\u7a0b\u3002\u8fd9\u79cd\u65b9\u6cd5\u7684\u4f18\u70b9\u5728\u4e8e\uff0c\u5bf9\u8c61\u53ef\u4ee5\u81ea\u7531\u5730\u66f4\u6539\u7ed9\u5b9a\u65b9\u6cd5\u5728\u540e\u53f0\u7684\u5b9e\u73b0\u65b9\u5f0f\u3002\u5b83\u8fd9\u6837\u505a\u4e0d\u4f1a\u7834\u574f\u4efb\u4f55\u4f7f\u7528\u5b83\u7684\u73b0\u6709\u4ee3\u7801\uff0c\u524d\u63d0\u662f\u8be5\u65b9\u6cd5\u7684\u53c2\u6570\u548c\u8fd4\u56de\u503c\u4fdd\u6301\u4e0d\u53d8\u3002<\/p>\n<h2>Encapsulation Using Traditional Accessors and Mutators<\/h2>\n<p>\u4f7f\u7528\u4f20\u7edf\u8bbf\u95ee\u5668\u548c\u7a81\u53d8\u5668\u8fdb\u884c\u5c01\u88c5<\/p>\n<p>Over the remaining pages in this chapter, you will be building a fairly complete class that models a general employee. To get the ball rolling, create a new Console Application project named EmployeeApp and create a new class file named Employee.cs. Update the Employee class with the following namespace, fields, methods, and constructors:<br \/>\n\u5728\u672c\u7ae0\u7684\u5176\u4f59\u9875\u9762\u4e2d\uff0c\u60a8\u5c06\u6784\u5efa\u4e00\u4e2a\u76f8\u5f53\u5b8c\u6574\u7684\u7c7b\u6765\u6a21\u62df\u4e00\u822c\u5458\u5de5\u3002\u82e5\u8981\u4f7f\u7403\u6eda\u52a8\uff0c\u8bf7\u521b\u5efa\u4e00\u4e2a\u540d\u4e3a EmployeeApp \u7684\u65b0\u63a7\u5236\u53f0\u5e94\u7528\u7a0b\u5e8f\u9879\u76ee\uff0c\u5e76\u521b\u5efa\u4e00\u4e2a\u540d\u4e3a Employee.cs \u7684\u65b0\u7c7b\u6587\u4ef6\u3002\u4f7f\u7528\u4ee5\u4e0b\u547d\u540d\u7a7a\u95f4\u3001\u5b57\u6bb5\u3001\u65b9\u6cd5\u548c\u6784\u9020\u51fd\u6570\u66f4\u65b0 Employee \u7c7b\uff1a<\/p>\n<p>namespace EmployeeApp; class Employee<br \/>\n{<br \/>\n\/\/ Field data.<br \/>\nprivate string _empName; private int _empId; private float _currPay;<\/p>\n<p>\/\/ Constructors.<br \/>\npublic Employee() {}<br \/>\npublic Employee(string name, int id, float pay)<\/p>\n<p>{<br \/>\n_empName = name;<br \/>\n_empId = id;<br \/>\n_currPay = pay;<br \/>\n}<\/p>\n<p>\/\/ Methods.<br \/>\npublic void GiveBonus(float amount) =&gt; _currPay += amount; public void DisplayStats()<br \/>\n{<br \/>\nConsole.WriteLine(&quot;Name: {0}&quot;, _empName);<br \/>\nConsole.WriteLine(&quot;ID: {0}&quot;, _empId);<br \/>\nConsole.WriteLine(&quot;Pay: {0}&quot;, _currPay);<br \/>\n}<br \/>\n}<\/p>\n<p>Notice that the fields of the Employee class are currently defined using the private keyword. Given this, the _empName, _empId, and _currPay fields are not directly accessible from an object variable. Therefore, the following logic in your code would result in compiler errors:<br \/>\n\u8bf7\u6ce8\u610f\uff0cEmployee \u7c7b\u7684\u5b57\u6bb5\u5f53\u524d\u662f\u4f7f\u7528\u79c1\u94a5\u5b9a\u4e49\u7684\u3002\u9274\u4e8e\u6b64\uff0c\u65e0\u6cd5\u4ece\u5bf9\u8c61\u53d8\u91cf\u76f4\u63a5\u8bbf\u95ee _empName\u3001_empId \u548c _currPay \u5b57\u6bb5\u3002\u56e0\u6b64\uff0c\u4ee3\u7801\u4e2d\u7684\u4ee5\u4e0b\u903b\u8f91\u5c06\u5bfc\u81f4\u7f16\u8bd1\u5668\u9519\u8bef\uff1a<\/p>\n<p>Employee emp = new Employee();<br \/>\n\/\/ Error! Cannot directly access private members<br \/>\n\/\/ from an object!<br \/>\nemp._empName = &quot;Marv&quot;;<\/p>\n<p>If you want the outside world to interact with a worker\u2019s full name, a traditional approach is to define an accessor (get method) and a mutator (set method). The role of a get method is to return to the caller the current value of the underlying state data. A set method allows the caller to change the current value of the underlying state data, as long as the defined business rules are met.<br \/>\n\u5982\u679c\u5e0c\u671b\u5916\u90e8\u4e16\u754c\u4e0e\u5de5\u4f5c\u4eba\u5458\u7684\u5168\u540d\u8fdb\u884c\u4ea4\u4e92\uff0c\u4f20\u7edf\u7684\u65b9\u6cd5\u662f\u5b9a\u4e49\u8bbf\u95ee\u5668\uff08get \u65b9\u6cd5\uff09\u548c\u7a81\u53d8\u5668\uff08set \u65b9\u6cd5\uff09\u3002get \u65b9\u6cd5\u7684\u4f5c\u7528\u662f\u5c06\u57fa\u7840\u72b6\u6001\u6570\u636e\u7684\u5f53\u524d\u503c\u8fd4\u56de\u7ed9\u8c03\u7528\u65b9\u3002\u53ea\u8981\u6ee1\u8db3\u5b9a\u4e49\u7684\u4e1a\u52a1\u89c4\u5219\uff0cset \u65b9\u6cd5\u5c31\u5141\u8bb8\u8c03\u7528\u65b9\u66f4\u6539\u57fa\u7840\u72b6\u6001\u6570\u636e\u7684\u5f53\u524d\u503c\u3002<\/p>\n<p>To illustrate, let\u2019s encapsulate the empName field. To do so, add the following public methods to the Employee class. Notice that the SetName() method performs a test on the incoming data to ensure the string is 15 characters or less. If it is not, an error prints to the console and returns without making a change to the empName field.<br \/>\n\u4e3a\u4e86\u8bf4\u660e\u8fd9\u4e00\u70b9\uff0c\u8ba9\u6211\u4eec\u5c01\u88c5 empName \u5b57\u6bb5\u3002\u4e3a\u6b64\uff0c\u8bf7\u5c06\u4ee5\u4e0b\u516c\u5171\u65b9\u6cd5\u6dfb\u52a0\u5230 Employee \u7c7b\u4e2d\u3002\u8bf7\u6ce8\u610f\uff0cSetName\uff08\uff09 \u65b9\u6cd5\u5bf9\u4f20\u5165\u6570\u636e\u6267\u884c\u6d4b\u8bd5\uff0c\u4ee5\u786e\u4fdd\u5b57\u7b26\u4e32\u4e0d\u8d85\u8fc7 15 \u4e2a\u5b57\u7b26\u3002\u5982\u679c\u4e0d\u662f\uff0c\u5219\u4f1a\u5c06\u9519\u8bef\u6253\u5370\u5230\u63a7\u5236\u53f0\u5e76\u8fd4\u56de\uff0c\u800c\u4e0d\u5bf9 empName \u5b57\u6bb5\u8fdb\u884c\u66f4\u6539\u3002<\/p>\n<p>\u25a0 Note  if this were a production-level class, you would probably also check the character length for an employee\u2019s name within your constructor logic. ignore this detail for the time being, as you will clean up this code in just a bit when you examine property syntax.<br \/>\n\u8bf7\u6ce8\u610f\uff0c\u5982\u679c\u8fd9\u662f\u4e00\u4e2a\u751f\u4ea7\u7ea7\u7c7b\uff0c\u60a8\u53ef\u80fd\u8fd8\u4f1a\u5728\u6784\u9020\u51fd\u6570\u903b\u8f91\u4e2d\u68c0\u67e5\u5458\u5de5\u59d3\u540d\u7684\u5b57\u7b26\u957f\u5ea6\u3002 \u6682\u65f6\u5ffd\u7565\u6b64\u7ec6\u8282\uff0c\u56e0\u4e3a\u5728\u68c0\u67e5\u5c5e\u6027\u8bed\u6cd5\u65f6\uff0c\u60a8\u5c06\u7a0d\u5fae\u6e05\u7406\u4e00\u4e0b\u6b64\u4ee3\u7801\u3002<\/p>\n<p>class Employee<br \/>\n{<br \/>\n\/\/ Field data.<br \/>\nprivate string _empName;<br \/>\n...<\/p>\n<p>\/\/ Accessor (get method).<br \/>\npublic string GetName() =&gt; _empName;<\/p>\n<p>\/\/ Mutator (set method).<br \/>\npublic void SetName(string name)<\/p>\n<p>{<br \/>\n\/\/ Do a check on incoming value<br \/>\n\/\/ before making assignment. if (name.Length &gt; 15)<br \/>\n{<br \/>\nConsole.WriteLine(&quot;Error! Name length exceeds 15 characters!&quot;);<br \/>\n}<br \/>\nelse<br \/>\n{<br \/>\n_empName = name;<br \/>\n}<br \/>\n}<br \/>\n}<br \/>\nThis technique requires two uniquely named methods to operate on a single data point. To test your new methods, update your code method as follows:<br \/>\n\u6b64\u6280\u672f\u9700\u8981\u4e24\u4e2a\u552f\u4e00\u547d\u540d\u7684\u65b9\u6cd5\u624d\u80fd\u5bf9\u5355\u4e2a\u6570\u636e\u70b9\u8fdb\u884c\u64cd\u4f5c\u3002\u82e5\u8981\u6d4b\u8bd5\u65b0\u65b9\u6cd5\uff0c\u8bf7\u6309\u5982\u4e0b\u6240\u793a\u66f4\u65b0\u4ee3\u7801\u65b9\u6cd5\uff1a<\/p>\n<p>using EmployeeApp;<\/p>\n<p>Console.WriteLine(&quot;<strong><strong><em> Fun with Encapsulation <\/em><\/strong><\/strong>\\n&quot;);<br \/>\nEmployee emp = new Employee(&quot;Marvin&quot;, 456, 30_000); emp.GiveBonus(1000);<br \/>\nemp.DisplayStats();<\/p>\n<p>\/\/ Use the get\/set methods to interact with the object's name. emp.SetName(&quot;Marv&quot;);<br \/>\nConsole.WriteLine(&quot;Employee is named: {0}&quot;, emp.GetName()); Console.ReadLine();<\/p>\n<p>Because of the code in your SetName() method, if you attempted to specify more than 15 characters (see the following), you would find the hard-coded error message printed to the console:<br \/>\n\u7531\u4e8e SetName\uff08\uff09 \u65b9\u6cd5\u4e2d\u7684\u4ee3\u7801\uff0c\u5982\u679c\u60a8\u5c1d\u8bd5\u6307\u5b9a\u8d85\u8fc7 15 \u4e2a\u5b57\u7b26\uff08\u8bf7\u53c2\u9605\u4ee5\u4e0b\u5185\u5bb9\uff09\uff0c\u60a8\u4f1a\u53d1\u73b0\u786c\u7f16\u7801\u7684\u9519\u8bef\u6d88\u606f\u6253\u5370\u5230\u63a7\u5236\u53f0\uff1a<\/p>\n<p>\/\/ Longer than 15 characters! Error will print to console.<br \/>\nEmployee emp2 = new Employee(); emp2.SetName(&quot;Xena the warrior princess&quot;);<\/p>\n<p>Console.ReadLine();<\/p>\n<p>So far, so good. You have encapsulated the private empName field using two public methods named GetName() and SetName(). If you were to further encapsulate the data in the Employee class, you would need to add various additional methods (such as GetID(), SetID(), GetCurrentPay(), SetCurrentPay()). Each of the mutator methods could also have various lines of code to check for additional business rules. While this could certainly be done, the C# language has a useful alternative notation to encapsulate class data.<br \/>\n\u76ee\u524d\u4e3a\u6b62\uff0c\u4e00\u5207\u90fd\u597d\u3002\u60a8\u5df2\u7ecf\u4f7f\u7528\u540d\u4e3a GetName\uff08\uff09 \u548c SetName\uff08\uff09 \u7684\u4e24\u4e2a\u516c\u5171\u65b9\u6cd5\u5c01\u88c5\u4e86\u79c1\u6709 empName \u5b57\u6bb5\u3002\u5982\u679c\u8981\u8fdb\u4e00\u6b65\u5c06\u6570\u636e\u5c01\u88c5\u5728 Employee \u7c7b\u4e2d\uff0c\u5219\u9700\u8981\u6dfb\u52a0\u5404\u79cd\u5176\u4ed6\u65b9\u6cd5\uff08\u4f8b\u5982 GetID\uff08\uff09\u3001SetID\uff08\uff09\u3001GetCurrentPay\uff08\uff09\u3001SetCurrentPay\uff08\uff09\uff09\u3002 \u6bcf\u4e2a\u7a81\u53d8\u5668\u65b9\u6cd5\u8fd8\u53ef\u4ee5\u6709\u5404\u79cd\u4ee3\u7801\u884c\u6765\u68c0\u67e5\u5176\u4ed6\u4e1a\u52a1\u89c4\u5219\u3002\u867d\u7136\u8fd9\u5f53\u7136\u53ef\u4ee5\u505a\u5230\uff0c\u4f46 C# \u8bed\u8a00\u6709\u4e00\u4e2a\u6709\u7528\u7684\u66ff\u4ee3\u8868\u793a\u6cd5\u6765\u5c01\u88c5\u7c7b\u6570\u636e\u3002<\/p>\n<h2>Encapsulation Using Properties<\/h2>\n<p>\u4f7f\u7528\u5c5e\u6027\u5c01\u88c5<\/p>\n<p>Although you can encapsulate a piece of field data using traditional get and set methods, .NET Core languages prefer to enforce data encapsulation state data using properties. First, understand that properties are just a container for \u201creal\u201d accessor and mutator methods, named get and set, respectively. Therefore, as a class designer, you are still able to perform any internal logic necessary before making the value assignment (e.g., uppercase the value, scrub the value for illegal characters, check the bounds of a numerical value, etc.).<br \/>\n\u5c3d\u7ba1\u53ef\u4ee5\u4f7f\u7528\u4f20\u7edf\u7684 get \u548c set \u65b9\u6cd5\u5c01\u88c5\u4e00\u6bb5\u5b57\u6bb5\u6570\u636e\uff0c\u4f46 .NET Core \u8bed\u8a00\u66f4\u559c\u6b22\u4f7f\u7528\u5c5e\u6027\u5f3a\u5236\u5b9e\u65bd\u6570\u636e\u5c01\u88c5\u72b6\u6001\u6570\u636e\u3002\u9996\u5148\uff0c\u4e86\u89e3\u5c5e\u6027\u53ea\u662f\u201c\u771f\u6b63\u7684\u201d\u8bbf\u95ee\u5668\u548c\u7a81\u53d8\u5668\u65b9\u6cd5\u7684\u5bb9\u5668\uff0c\u5206\u522b\u547d\u540d\u4e3a get \u548c set\u3002\u56e0\u6b64\uff0c\u4f5c\u4e3a\u7c7b\u8bbe\u8ba1\u8005\uff0c\u60a8\u4ecd\u7136\u53ef\u4ee5\u5728\u8fdb\u884c\u503c\u8d4b\u503c\u4e4b\u524d\u6267\u884c\u4efb\u4f55\u5fc5\u8981\u7684\u5185\u90e8\u903b\u8f91\uff08\u4f8b\u5982\uff0c\u5927\u5199\u503c\u3001\u6e05\u9664\u975e\u6cd5\u5b57\u7b26\u7684\u503c\u3001\u68c0\u67e5\u6570\u503c\u7684\u8fb9\u754c\u7b49\uff09\u3002<\/p>\n<p>Here is the updated Employee class, now enforcing encapsulation of each field using property syntax rather than traditional get and set methods:<br \/>\n\u4e0b\u9762\u662f\u66f4\u65b0\u7684 Employee \u7c7b\uff0c\u73b0\u5728\u4f7f\u7528\u5c5e\u6027\u8bed\u6cd5\u800c\u4e0d\u662f\u4f20\u7edf\u7684 get \u548c set \u65b9\u6cd5\u5f3a\u5236\u5c01\u88c5\u6bcf\u4e2a\u5b57\u6bb5\uff1a<\/p>\n<p>class Employee<br \/>\n{<br \/>\n\/\/ Field data.<br \/>\nprivate string _empName; private int _empId; private float _currPay;<br \/>\n\/\/ Properties! public string Name<br \/>\n{<br \/>\nget { return _empName; } set<br \/>\n{<br \/>\nif (value.Length &gt; 15)<br \/>\n{<br \/>\nConsole.WriteLine(&quot;Error! Name length exceeds 15 characters!&quot;);<br \/>\n}<br \/>\nelse<br \/>\n{<br \/>\n_empName = value;<br \/>\n}<br \/>\n}<br \/>\n}<br \/>\n\/\/ We could add additional business rules to the sets of these properties;<br \/>\n\/\/ however, there is no need to do so for this example.<br \/>\npublic int Id<br \/>\n{<br \/>\nget { return _empId; } set { _empId = value; }<br \/>\n}<br \/>\npublic float Pay<br \/>\n{<\/p>\n<p>}<br \/>\n...<br \/>\n}<\/p>\n<p>get { return _currPay; } set { _currPay = value; }<\/p>\n<p>A C# property is composed by defining a get scope (accessor) and set scope (mutator) directly within the property itself. Notice that the property specifies the type of data it is encapsulating by what appears to be a return value. Also take note that, unlike a method, properties do not make use of parentheses (not even empty parentheses) when being defined. Consider the following commentary on your current Id property:<br \/>\nC# \u5c5e\u6027\u662f\u901a\u8fc7\u76f4\u63a5\u5728\u5c5e\u6027\u672c\u8eab\u4e2d\u5b9a\u4e49 get \u8303\u56f4\uff08\u8bbf\u95ee\u5668\uff09\u548c\u8bbe\u7f6e\u8303\u56f4\uff08\u7a81\u53d8\u5668\uff09\u6765\u7ec4\u6210\u7684\u3002\u8bf7\u6ce8\u610f\uff0c\u8be5\u5c5e\u6027\u6307\u5b9a\u5b83\u901a\u8fc7\u770b\u4f3c\u8fd4\u56de\u503c\u7684\u5185\u5bb9\u5c01\u88c5\u7684\u6570\u636e\u7c7b\u578b\u3002\u53e6\u8bf7\u6ce8\u610f\uff0c\u4e0e\u65b9\u6cd5\u4e0d\u540c\uff0c\u5c5e\u6027\u5728\u5b9a\u4e49\u65f6\u4e0d\u4f7f\u7528\u62ec\u53f7\uff08\u751a\u81f3\u4e0d\u4f7f\u7528\u7a7a\u62ec\u53f7\uff09\u3002\u8bf7\u8003\u8651\u4ee5\u4e0b\u6709\u5173\u5f53\u524d Id \u5c5e\u6027\u7684\u6ce8\u91ca\uff1a<\/p>\n<p>\/\/ The 'int' represents the type of data this property encapsulates. public int Id \/\/ Note lack of parentheses.<br \/>\n{<br \/>\nget { return _empId; } set { _empID = value; }<br \/>\n}<\/p>\n<p>Within a set scope of a property, you use a token named value, which is used to represent the incoming value used to assign the property by the caller. This token is not a true C# keyword but is what is known as<br \/>\na contextual keyword. When the token value is within the set scope of the property, it always represents the value being assigned by the caller, and it will always be the same underlying data type as the property itself. Thus, notice how the Name property can still test the range of the string as so:<br \/>\n\u5728\u5c5e\u6027\u7684\u8bbe\u7f6e\u8303\u56f4\u5185\uff0c\u4f7f\u7528\u540d\u4e3a value \u7684\u4ee4\u724c\uff0c\u8be5\u4ee4\u724c\u7528\u4e8e\u8868\u793a\u8c03\u7528\u65b9\u7528\u4e8e\u5206\u914d\u5c5e\u6027\u7684\u4f20\u5165\u503c\u3002\u6b64\u6807\u8bb0\u4e0d\u662f\u771f\u6b63\u7684 C# \u5173\u952e\u5b57\uff0c\u800c\u662f\u6240\u8c13\u7684<\/p>\n<p>\u4e0a\u4e0b\u6587\u5173\u952e\u5b57\u3002\u5f53\u4ee4\u724c\u503c\u5728\u5c5e\u6027\u7684\u8bbe\u7f6e\u8303\u56f4\u5185\u65f6\uff0c\u5b83\u59cb\u7ec8\u8868\u793a\u8c03\u7528\u65b9\u5206\u914d\u7684\u503c\uff0c\u5e76\u4e14\u5b83\u5c06\u59cb\u7ec8\u4e0e\u5c5e\u6027\u672c\u8eab\u76f8\u540c\u7684\u57fa\u7840\u6570\u636e\u7c7b\u578b\u3002\u56e0\u6b64\uff0c\u8bf7\u6ce8\u610f Name \u5c5e\u6027\u5982\u4f55\u4ecd\u53ef\u4ee5\u6d4b\u8bd5\u5b57\u7b26\u4e32\u7684\u8303\u56f4\uff0c\u5982\u4e0b\u6240\u793a\uff1a<\/p>\n<p>public string Name<br \/>\n{<br \/>\nget { return _empName; } set<br \/>\n{<br \/>\n\/\/ Here, value is really a string. if (value.Length &gt; 15)<br \/>\n{   Console.WriteLine(&quot;Error! Name length exceeds 15 characters!&quot;);<br \/>\n}<br \/>\nelse<br \/>\n{<br \/>\nempName = value;<br \/>\n}<br \/>\n}<br \/>\n}<\/p>\n<p>After you have these properties in place, it appears to the caller that it is getting and setting a public point of data; however, the correct get and set block is called behind the scenes to preserve encapsulation.<br \/>\n\u62e5\u6709\u8fd9\u4e9b\u5c5e\u6027\u540e\uff0c\u8c03\u7528\u65b9\u4f3c\u4e4e\u6b63\u5728\u83b7\u53d6\u548c\u8bbe\u7f6e\u516c\u5171\u6570\u636e\u70b9;\u4f46\u662f\uff0c\u5728\u540e\u53f0\u8c03\u7528\u6b63\u786e\u7684\u83b7\u53d6\u548c\u8bbe\u7f6e\u5757\u4ee5\u4fdd\u7559\u5c01\u88c5\u3002<\/p>\n<p>using EmployeeApp;<br \/>\nConsole.WriteLine(&quot;<strong><strong><em> Fun with Encapsulation <\/em><\/strong><\/strong>\\n&quot;); Employee emp = new Employee(&quot;Marvin&quot;, 456, 30000); emp.GiveBonus(1000);<br \/>\nemp.DisplayStats();<\/p>\n<p>\/\/ Reset and then get the Name property.<br \/>\nemp.Name = &quot;Marv&quot;;<br \/>\nConsole.WriteLine(&quot;Employee is named: {0}&quot;, emp.Name); Console.ReadLine();<\/p>\n<p>Properties (as opposed to accessor and mutator methods) also make your types easier to manipulate, in that properties are able to respond to the intrinsic operators of C#. To illustrate, assume that the Employee class type has an internal private member variable representing the age of the employee. Here is the relevant update (notice the use of constructor chaining):<br \/>\n\u5c5e\u6027\uff08\u4e0e\u8bbf\u95ee\u5668\u548c\u7a81\u53d8\u5668\u65b9\u6cd5\u76f8\u53cd\uff09\u4e5f\u4f7f\u7c7b\u578b\u66f4\u6613\u4e8e\u64cd\u4f5c\uff0c\u56e0\u4e3a\u5c5e\u6027\u80fd\u591f\u54cd\u5e94 C# \u7684\u5185\u90e8\u8fd0\u7b97\u7b26\u3002\u4e3a\u4e86\u8bf4\u660e\u8fd9\u4e00\u70b9\uff0c\u5047\u5b9a Employee \u7c7b\u7c7b\u578b\u5177\u6709\u4e00\u4e2a\u8868\u793a\u96c7\u5458\u5e74\u9f84\u7684\u5185\u90e8\u79c1\u6709\u6210\u5458\u53d8\u91cf\u3002\u4ee5\u4e0b\u662f\u76f8\u5173\u66f4\u65b0\uff08\u8bf7\u6ce8\u610f\u6784\u9020\u51fd\u6570\u94fe\u63a5\u7684\u4f7f\u7528\uff09\uff1a<\/p>\n<p>class Employee<br \/>\n{<br \/>\n...<br \/>\n\/\/ New field and property. private int _empAge; public int Age<br \/>\n{<br \/>\nget { return _empAge; } set { _empAge = value; }<br \/>\n}<\/p>\n<p>\/\/ Updated constructors.<br \/>\npublic Employee() {}<br \/>\npublic Employee(string name, int id, float pay)<br \/>\n:this(name, 0, id, pay){}<\/p>\n<p>public Employee(string name, int age, int id, float pay)<br \/>\n{<br \/>\n_empName = name;<br \/>\n_empId = id;<br \/>\n_empAge = age;<br \/>\n_currPay = pay;<br \/>\n}<\/p>\n<p>\/\/ Updated DisplayStats() method now accounts for age.<br \/>\npublic void DisplayStats()<br \/>\n{<br \/>\nConsole.WriteLine(&quot;Name: {0}&quot;, _empName);<br \/>\nConsole.WriteLine(&quot;ID: {0}&quot;, _empId);<br \/>\nConsole.WriteLine(&quot;Age: {0}&quot;, _empAge);<br \/>\nConsole.WriteLine(&quot;Pay: {0}&quot;, _currPay);<br \/>\n}<br \/>\n}<\/p>\n<p>Now assume you have created an Employee object named joe. On his birthday, you want to increment the age by one. Using traditional accessor and mutator methods, you would need to write code such as the following:<br \/>\n\u73b0\u5728\u5047\u8bbe\u60a8\u5df2\u7ecf\u521b\u5efa\u4e86\u4e00\u4e2a\u540d\u4e3a joe \u7684 Employee \u5bf9\u8c61\u3002\u5728\u4ed6\u751f\u65e5\u90a3\u5929\uff0c\u4f60\u60f3\u628a\u5e74\u9f84\u589e\u52a0\u4e00\u4e2a\u3002\u4f7f\u7528\u4f20\u7edf\u7684\u8bbf\u95ee\u5668\u548c\u7a81\u53d8\u5668\u65b9\u6cd5\uff0c\u60a8\u9700\u8981\u7f16\u5199\u5982\u4e0b\u4ee3\u7801\uff1a<\/p>\n<p>Employee joe = new Employee(); joe.SetAge(joe.GetAge() + 1);<\/p>\n<p>However, if you encapsulate empAge using a property named Age, you are able to simply write this:<br \/>\n\u4f46\u662f\uff0c\u5982\u679c\u4f7f\u7528\u540d\u4e3a Age \u7684\u5c5e\u6027\u5c01\u88c5 empAge\uff0c\u5219\u53ef\u4ee5\u7b80\u5355\u5730\u7f16\u5199\u4ee5\u4e0b\u5185\u5bb9\uff1a<\/p>\n<p>Employee joe = new Employee(); joe.Age++;<\/p>\n<h2>Properties As Expression-Bodied Members (New 7.0)<\/h2>\n<p>\u4f5c\u4e3a\u8868\u8fbe\u5f0f\u4e3b\u4f53\u6210\u5458\u7684\u5c5e\u6027\uff08\u65b0 7.0\uff09<br \/>\nAs mentioned previously, property get and set accessors can also be written as expression-bodied members. The rules and syntax are the same: single-line methods can be written using the new syntax. So, the Age property could be written like this:<br \/>\n\u5982\u524d\u6240\u8ff0\uff0c\u5c5e\u6027 get \u548c set \u8bbf\u95ee\u5668\u4e5f\u53ef\u4ee5\u7f16\u5199\u4e3a\u8868\u8fbe\u5f0f\u4f53\u6210\u5458\u3002\u89c4\u5219\u548c\u8bed\u6cd5\u662f\u76f8\u540c\u7684\uff1a\u53ef\u4ee5\u4f7f\u7528\u65b0\u8bed\u6cd5\u7f16\u5199\u5355\u884c\u65b9\u6cd5\u3002\u56e0\u6b64\uff0cAge \u5c5e\u6027\u53ef\u4ee5\u8fd9\u6837\u5199\uff1a<\/p>\n<p>public int Age<br \/>\n{<br \/>\nget =&gt; empAge;<br \/>\nset =&gt; empAge = value;<br \/>\n}<\/p>\n<p>Both syntaxes compile down to the same IL, so which syntax you use is completely up to you. In this text, you will see a mix of both styles to keep visibility on them, not because I am adhering to a specific code style.<br \/>\n\u8fd9\u4e24\u79cd\u8bed\u6cd5\u90fd\u7f16\u8bd1\u4e3a\u540c\u4e00\u4e2a IL\uff0c\u56e0\u6b64\u4f7f\u7528\u54ea\u79cd\u8bed\u6cd5\u5b8c\u5168\u53d6\u51b3\u4e8e\u60a8\u3002\u5728\u672c\u6587\u4e2d\uff0c\u60a8\u5c06\u770b\u5230\u4e24\u79cd\u6837\u5f0f\u7684\u6df7\u5408\uff0c\u4ee5\u4fdd\u6301\u5b83\u4eec\u7684\u53ef\u89c1\u6027\uff0c\u800c\u4e0d\u662f\u56e0\u4e3a\u6211\u575a\u6301\u7279\u5b9a\u7684\u4ee3\u7801\u6837\u5f0f\u3002<\/p>\n<h2>Using Properties Within a Class Definition<\/h2>\n<p>\u5728\u7c7b\u5b9a\u4e49\u4e2d\u4f7f\u7528\u5c5e\u6027<\/p>\n<p>Properties, specifically the set portion of a property, are common places to package up the business rules of your class. Currently, the Employee class has a Name property that ensures the name is no more than 15 characters. The remaining properties (ID, Pay, and Age) could also be updated with any relevant logic.<br \/>\nWhile this is well and good, also consider what a class constructor typically does internally. It will take the incoming parameters, check for valid data, and then make assignments to the internal private fields.<br \/>\n\u5c5e\u6027\uff08\u7279\u522b\u662f\u5c5e\u6027\u7684\u96c6\u5408\u90e8\u5206\uff09\u662f\u6253\u5305\u7c7b\u7684\u4e1a\u52a1\u89c4\u5219\u7684\u5e38\u7528\u4f4d\u7f6e\u3002\u76ee\u524d\uff0cEmployee \u7c7b\u5177\u6709\u4e00\u4e2a Name \u5c5e\u6027\uff0c\u8be5\u5c5e\u6027\u53ef\u786e\u4fdd\u540d\u79f0\u4e0d\u8d85\u8fc7 15 \u4e2a\u5b57\u7b26\u3002\u5176\u4f59\u5c5e\u6027\uff08ID\u3001\u652f\u4ed8\u548c\u5e74\u9f84\uff09\u4e5f\u53ef\u4ee5\u4f7f\u7528\u4efb\u4f55\u76f8\u5173\u903b\u8f91\u8fdb\u884c\u66f4\u65b0\u3002\u867d\u7136\u8fd9\u5f88\u597d\uff0c\u4f46\u4e5f\u8981\u8003\u8651\u7c7b\u6784\u9020\u51fd\u6570\u901a\u5e38\u5728\u5185\u90e8\u505a\u4ec0\u4e48\u3002\u5b83\u5c06\u91c7\u7528\u4f20\u5165\u7684\u53c2\u6570\uff0c\u68c0\u67e5\u6709\u6548\u6570\u636e\uff0c\u7136\u540e\u5206\u914d\u7ed9\u5185\u90e8\u79c1\u6709\u5b57\u6bb5\u3002<\/p>\n<p>Currently, your master constructor does not test the incoming string data for a valid range, so you could update this member as so:<br \/>\n\u76ee\u524d\uff0c\u4e3b\u6784\u9020\u51fd\u6570\u4e0d\u4f1a\u6d4b\u8bd5\u6709\u6548\u8303\u56f4\u7684\u4f20\u5165\u5b57\u7b26\u4e32\u6570\u636e\uff0c\u56e0\u6b64\u53ef\u4ee5\u6309\u5982\u4e0b\u6240\u793a\u66f4\u65b0\u6b64\u6210\u5458\uff1a<\/p>\n<p>public Employee(string name, int age, int id, float pay)<br \/>\n{<br \/>\n\/\/ Humm, this seems like a problem... if (name.Length &gt; 15)<br \/>\n{<br \/>\nConsole.WriteLine(&quot;Error! Name length exceeds 15 characters!&quot;);<br \/>\n}<br \/>\nelse<br \/>\n{<br \/>\n_empName = name;<br \/>\n}<br \/>\n_empId = id;<br \/>\n_empAge = age;<br \/>\n_currPay = pay;<br \/>\n}<\/p>\n<p>I am sure you can see the problem with this approach. The Name property and your master constructor are performing the same error checking. If you were also making checks on the other data points, you would have a good deal of duplicate code. To streamline your code and isolate all of your error checking to a central location, you will do well if you always use properties within your class whenever you need to get or set the values. Consider the following updated constructor:<br \/>\n\u6211\u76f8\u4fe1\u4f60\u53ef\u4ee5\u770b\u5230\u8fd9\u79cd\u65b9\u6cd5\u7684\u95ee\u9898\u3002Name \u5c5e\u6027\u548c\u4e3b\u6784\u9020\u51fd\u6570\u6b63\u5728\u6267\u884c\u76f8\u540c\u7684\u9519\u8bef\u68c0\u67e5\u3002\u5982\u679c\u60a8\u8fd8\u5bf9\u5176\u4ed6\u6570\u636e\u70b9\u8fdb\u884c\u68c0\u67e5\uff0c\u5219\u4f1a\u6709\u5927\u91cf\u91cd\u590d\u7684\u4ee3\u7801\u3002\u4e3a\u4e86\u7b80\u5316\u4ee3\u7801\u5e76\u5c06\u6240\u6709\u9519\u8bef\u68c0\u67e5\u9694\u79bb\u5230\u4e00\u4e2a\u4e2d\u5fc3\u4f4d\u7f6e\uff0c\u5982\u679c\u5728\u9700\u8981\u83b7\u53d6\u6216\u8bbe\u7f6e\u503c\u65f6\u59cb\u7ec8\u4f7f\u7528\u7c7b\u4e2d\u7684\u5c5e\u6027\uff0c\u5219\u4f1a\u505a\u5f97\u5f88\u597d\u3002\u8bf7\u8003\u8651\u4ee5\u4e0b\u66f4\u65b0\u7684\u6784\u9020\u51fd\u6570\uff1a<\/p>\n<p>public Employee(string name, int age, int id, float pay)<br \/>\n{<br \/>\n\/\/ Better! Use properties when setting class data.<br \/>\n\/\/ This reduces the amount of duplicate error checks. Name = name;<br \/>\nAge = age;<br \/>\nID = id;<br \/>\nPay = pay;<br \/>\n}<\/p>\n<p>Beyond updating constructors to use properties when assigning values, it is good practice to use properties throughout a class implementation to ensure your business rules are always enforced. In many cases, the only time when you directly refer to the underlying private piece of data is within the property itself. With this in mind, here is your updated Employee class:<br \/>\n\u9664\u4e86\u5728\u5206\u914d\u503c\u65f6\u66f4\u65b0\u6784\u9020\u51fd\u6570\u4ee5\u4f7f\u7528\u5c5e\u6027\u4e4b\u5916\uff0c\u6700\u597d\u5728\u6574\u4e2a\u7c7b\u5b9e\u73b0\u4e2d\u4f7f\u7528\u5c5e\u6027\uff0c\u4ee5\u786e\u4fdd\u59cb\u7ec8\u5f3a\u5236\u5b9e\u65bd\u4e1a\u52a1\u89c4\u5219\u3002\u5728\u8bb8\u591a\u60c5\u51b5\u4e0b\uff0c\u76f4\u63a5\u5f15\u7528\u57fa\u7840\u79c1\u6709\u6570\u636e\u7684\u552f\u4e00\u65f6\u95f4\u662f\u5728\u5c5e\u6027\u672c\u8eab\u5185\u3002\u8003\u8651\u5230\u8fd9\u4e00\u70b9\uff0c\u4ee5\u4e0b\u662f\u60a8\u66f4\u65b0\u7684\u5458\u5de5\u7c7b\uff1a<\/p>\n<p>class Employee<br \/>\n{<br \/>\n\/\/ Field data.<br \/>\nprivate string _empName; private int _empId;<\/p>\n<p>private float _currPay; private int _empAge;<br \/>\n\/\/ Constructors.<br \/>\npublic Employee() { }<br \/>\npublic Employee(string name, int id, float pay)<br \/>\n:this(name, 0, id, pay){}<br \/>\npublic Employee(string name, int age, int id, float pay)<br \/>\n{<br \/>\nName = name;<br \/>\nAge = age;<br \/>\nID = id;<br \/>\nPay = pay;<br \/>\n}<br \/>\n\/\/ Methods.<br \/>\npublic void GiveBonus(float amount) =&gt; Pay += amount;<\/p>\n<p>public void DisplayStats()<br \/>\n{<br \/>\nConsole.WriteLine(&quot;Name: {0}&quot;, Name);<br \/>\nConsole.WriteLine(&quot;ID: {0}&quot;, Id);<br \/>\nConsole.WriteLine(&quot;Age: {0}&quot;, Age);<br \/>\nConsole.WriteLine(&quot;Pay: {0}&quot;, Pay);<br \/>\n}<\/p>\n<p>\/\/ Properties as before...<br \/>\n...<br \/>\n}<\/p>\n<h2>Read-Only Properties<\/h2>\n<p>\u53ea\u8bfb\u5c5e\u6027<br \/>\nWhen encapsulating data, you might want to configure a read-only property. To do so, simply omit the set block. For example, assume you have a new property named SocialSecurityNumber, which encapsulates a private string variable named empSSN. If you want to make this a read-only property, you could write this:<br \/>\n\u5c01\u88c5\u6570\u636e\u65f6\uff0c\u53ef\u80fd\u9700\u8981\u914d\u7f6e\u53ea\u8bfb\u5c5e\u6027\u3002\u4e3a\u6b64\uff0c\u53ea\u9700\u7701\u7565\u8bbe\u7f6e\u5757\u5373\u53ef\u3002\u4f8b\u5982\uff0c\u5047\u8bbe\u60a8\u6709\u4e00\u4e2a\u540d\u4e3a SocialSecurityNumber \u7684\u65b0\u5c5e\u6027\uff0c\u8be5\u5c5e\u6027\u5c01\u88c5\u4e86\u4e00\u4e2a\u540d\u4e3a empSSN \u7684\u79c1\u6709\u5b57\u7b26\u4e32\u53d8\u91cf\u3002\u5982\u679c\u8981\u5c06\u5176\u8bbe\u7f6e\u4e3a\u53ea\u8bfb\u5c5e\u6027\uff0c\u53ef\u4ee5\u7f16\u5199\u4ee5\u4e0b\u5185\u5bb9\uff1a<\/p>\n<p>public string SocialSecurityNumber<br \/>\n{<br \/>\nget { return _empSSN; }<br \/>\n}<\/p>\n<p>Properties that only have a getter can also be simplified using expression body members. The following line is equivalent to the previous code block:<br \/>\n\u53ea\u6709 getter \u7684\u5c5e\u6027\u4e5f\u53ef\u4ee5\u4f7f\u7528\u8868\u8fbe\u5f0f\u4e3b\u4f53\u6210\u5458\u8fdb\u884c\u7b80\u5316\u3002\u4ee5\u4e0b\u884c\u7b49\u6548\u4e8e\u524d\u9762\u7684\u4ee3\u7801\u5757\uff1a<\/p>\n<p>public string SocialSecurityNumber =&gt; _empSSN;<\/p>\n<p>Now assume your class constructor has a new parameter to let the caller set the SSN of the object. Since the SocialSecurityNumber property is read-only, you cannot set the value as so:<br \/>\n\u73b0\u5728\u5047\u8bbe\u60a8\u7684\u7c7b\u6784\u9020\u51fd\u6570\u6709\u4e00\u4e2a\u65b0\u53c2\u6570\uff0c\u8ba9\u8c03\u7528\u65b9\u8bbe\u7f6e\u5bf9\u8c61\u7684 SSN\u3002\u7531\u4e8e SocialSecurityNumber \u5c5e\u6027\u662f\u53ea\u8bfb\u7684\uff0c\u56e0\u6b64\u4e0d\u80fd\u6309\u4ee5\u4e0b\u65b9\u5f0f\u8bbe\u7f6e\u8be5\u503c\uff1a<\/p>\n<p>public Employee(string name, int age, int id, float pay, string ssn)<br \/>\n{<br \/>\nName = name;<br \/>\nAge = age;<\/p>\n<p>ID = id;<br \/>\nPay = pay;<\/p>\n<p>\/\/ OOPS! This is no longer possible if the property is read only. SocialSecurityNumber = ssn;<br \/>\n}<\/p>\n<p>Unless you are willing to redesign the property as read-write (which you will do soon), your only choice with read-only properties would be to use the underlying empSSN member variable within your constructor logic as so:<br \/>\n\u9664\u975e\u60a8\u613f\u610f\u5c06\u5c5e\u6027\u91cd\u65b0\u8bbe\u8ba1\u4e3a\u8bfb\u5199\uff08\u60a8\u5f88\u5feb\u5c31\u4f1a\u8fd9\u6837\u505a\uff09\uff0c\u5426\u5219\u4f7f\u7528\u53ea\u8bfb\u5c5e\u6027\u7684\u552f\u4e00\u9009\u62e9\u662f\u5728\u6784\u9020\u51fd\u6570\u903b\u8f91\u4e2d\u4f7f\u7528\u5e95\u5c42 empSSN \u6210\u5458\u53d8\u91cf\uff0c\u5982\u4e0b\u6240\u793a\uff1a<\/p>\n<p>public Employee(string name, int age, int id, float pay, string ssn)<br \/>\n{<br \/>\n...<br \/>\n\/\/ Check incoming ssn parameter as required and then set the value. empSSN = ssn;<br \/>\n}<\/p>\n<h2>Write-Only Properties<\/h2>\n<p>\u53ea\u5199\u5c5e\u6027<\/p>\n<p>If you want to configure your property as a write-only property, omit the get block, like this:<br \/>\n\u5982\u679c\u8981\u5c06\u5c5e\u6027\u914d\u7f6e\u4e3a\u53ea\u5199\u5c5e\u6027\uff0c\u8bf7\u7701\u7565 get \u5757\uff0c\u5982\u4e0b\u6240\u793a\uff1a<\/p>\n<p>public int Id<br \/>\n{<br \/>\nset { _empId = value; }<br \/>\n}<\/p>\n<h2>Mixing Private and Public Get\/Set Methods on Properties<\/h2>\n<p>\u5728\u5c5e\u6027\u4e0a\u6df7\u5408\u4f7f\u7528\u79c1\u6709\u548c\u516c\u5171\u83b7\u53d6\/\u8bbe\u7f6e\u65b9\u6cd5<\/p>\n<p>When defining properties, the access level for the get and set methods can be different. Revisiting the Social Security number, if the goal is to prevent the modification of the number from outside the class, then declare the get method as public but the set method as private, like this:<br \/>\n\u5b9a\u4e49\u5c5e\u6027\u65f6\uff0cget \u548c set \u65b9\u6cd5\u7684\u8bbf\u95ee\u7ea7\u522b\u53ef\u4ee5\u4e0d\u540c\u3002\u91cd\u65b0\u8bbf\u95ee\u793e\u4f1a\u4fdd\u9669\u53f7\uff0c\u5982\u679c\u76ee\u6807\u662f\u9632\u6b62\u4ece\u7c7b\u5916\u90e8\u4fee\u6539\u53f7\u7801\uff0c\u5219\u5c06 get \u65b9\u6cd5\u58f0\u660e\u4e3a\u516c\u5171\uff0c\u4f46\u5c06 set \u65b9\u6cd5\u58f0\u660e\u4e3a\u79c1\u6709\uff0c\u5982\u4e0b\u6240\u793a\uff1a<\/p>\n<p>public string SocialSecurityNumber<br \/>\n{<br \/>\nget =&gt; _empSSN;<br \/>\nprivate set =&gt; _empSSN = value;<br \/>\n}<\/p>\n<p>Note that this changes the property from read-only to read-write. The difference is that the write is hidden from anything outside the defining class.<br \/>\n\u8bf7\u6ce8\u610f\uff0c\u8fd9\u4f1a\u5c06\u5c5e\u6027\u4ece\u53ea\u8bfb\u66f4\u6539\u4e3a\u8bfb\u5199\u3002\u4e0d\u540c\u4e4b\u5904\u5728\u4e8e\uff0c\u5199\u5165\u5bf9\u5b9a\u4e49\u7c7b\u4e4b\u5916\u7684\u4efb\u4f55\u5185\u5bb9\u90fd\u662f\u9690\u85cf\u7684\u3002<\/p>\n<h2>Revisiting the static Keyword: Defining Static Properties<\/h2>\n<p>\u91cd\u65b0\u8bbf\u95ee\u9759\u6001\u5173\u952e\u5b57\uff1a\u5b9a\u4e49\u9759\u6001\u5c5e\u6027<\/p>\n<p>Earlier in this chapter, you examined the role of the static keyword. Now that you understand the use of C# property syntax, you can formalize static properties. In the StaticDataAndMembers project created earlier in this chapter, your SavingsAccount class had two public static methods to get and set the interest rate.<br \/>\n\u5728\u672c\u7ae0\u524d\u9762\uff0c\u60a8\u7814\u7a76\u4e86\u9759\u6001\u5173\u952e\u5b57\u7684\u4f5c\u7528\u3002\u4e86\u89e3 C# \u5c5e\u6027\u8bed\u6cd5\u7684\u7528\u6cd5\u540e\uff0c\u53ef\u4ee5\u5f62\u5f0f\u5316\u9759\u6001\u5c5e\u6027\u3002\u5728\u672c\u7ae0\u524d\u9762\u521b\u5efa\u7684 StaticDataAndMembers \u9879\u76ee\u4e2d\uff0cSavings Account \u7c7b\u6709\u4e24\u4e2a\u516c\u5171\u9759\u6001\u65b9\u6cd5\u6765\u83b7\u53d6\u548c\u8bbe\u7f6e\u5229\u7387\u3002<\/p>\n<p>However, it would be more standard to wrap this data point in a static property. Here is an example (note the use of the static keyword):<br \/>\n\u4f46\u662f\uff0c\u5c06\u6b64\u6570\u636e\u70b9\u5305\u88c5\u5728\u9759\u6001\u5c5e\u6027\u4e2d\u4f1a\u66f4\u6807\u51c6\u3002\u4e0b\u9762\u662f\u4e00\u4e2a\u793a\u4f8b\uff08\u8bf7\u6ce8\u610f\u9759\u6001\u5173\u952e\u5b57\u7684\u4f7f\u7528\uff09\uff1a<\/p>\n<p>\/\/ A simple savings account class. class SavingsAccount<br \/>\n{<br \/>\n\/\/ Instance-level data. public double currBalance;<\/p>\n<p>\/\/ A static point of data.<br \/>\nprivate static double _currInterestRate = 0.04;<\/p>\n<p>\/\/ A static property.<br \/>\npublic static double InterestRate<br \/>\n{<\/p>\n<p>}<br \/>\n...<br \/>\n}<\/p>\n<p>get { return _currInterestRate; } set { _currInterestRate = value; }<\/p>\n<p>If you want to use this property in place of the previous static methods, you could update your code as so:<br \/>\n\u5982\u679c\u8981\u4f7f\u7528\u6b64\u5c5e\u6027\u4ee3\u66ff\u4ee5\u524d\u7684\u9759\u6001\u65b9\u6cd5\uff0c\u53ef\u4ee5\u6309\u5982\u4e0b\u6240\u793a\u66f4\u65b0\u4ee3\u7801\uff1a<\/p>\n<p>\/\/ Print the current interest rate via property. Console.WriteLine(&quot;Interest Rate is: {0}&quot;, SavingsAccount.InterestRate);<\/p>\n<h2>Pattern Matching with Property Patterns (New 8.0)<\/h2>\n<p>\u6a21\u5f0f\u4e0e\u5c5e\u6027\u6a21\u5f0f\u5339\u914d\uff08\u65b0 8.0\uff09<\/p>\n<p>The property pattern matches an expression when an expression result is non-null and every nested pattern matches the corresponding property or field of the expression result. In other words, the property pattern enables you to match on properties of an object. To set up the example, add a new file (EmployeePayTypeEnum.cs) to the EmployeeApp project for an enumeration of employee pay types, as follows:<br \/>\n\u5f53\u8868\u8fbe\u5f0f\u7ed3\u679c\u4e3a\u975e null \u5e76\u4e14\u6bcf\u4e2a\u5d4c\u5957\u6a21\u5f0f\u90fd\u4e0e\u8868\u8fbe\u5f0f\u7ed3\u679c\u7684\u76f8\u5e94\u5c5e\u6027\u6216\u5b57\u6bb5\u5339\u914d\u65f6\uff0c\u5c5e\u6027\u6a21\u5f0f\u4e0e\u8868\u8fbe\u5f0f\u5339\u914d\u3002\u6362\u53e5\u8bdd\u8bf4\uff0c\u5c5e\u6027\u6a21\u5f0f\u4f7f\u60a8\u80fd\u591f\u5339\u914d\u5bf9\u8c61\u7684\u5c5e\u6027\u3002\u82e5\u8981\u8bbe\u7f6e\u8be5\u793a\u4f8b\uff0c\u8bf7\u5c06\u4e00\u4e2a\u65b0\u6587\u4ef6 \uff08EmployeePayTypeEnum.cs\uff09 \u6dfb\u52a0\u5230 EmployeeApp \u9879\u76ee\u4e2d\uff0c\u4ee5\u679a\u4e3e\u5458\u5de5\u4ed8\u85aa\u7c7b\u578b\uff0c\u5982\u4e0b\u6240\u793a\uff1a<\/p>\n<p>namespace EmployeeApp;<br \/>\npublic enum EmployeePayTypeEnum<br \/>\n{<br \/>\nHourly, Salaried, Commission<br \/>\n}<\/p>\n<p>Update the Employee class with a property for the pay type and initialize it from the constructor. The relevant code changes are listed here:<br \/>\n\u4f7f\u7528\u652f\u4ed8\u7c7b\u578b\u7684\u5c5e\u6027\u66f4\u65b0 Employee \u7c7b\uff0c\u5e76\u4ece\u6784\u9020\u51fd\u6570\u521d\u59cb\u5316\u5b83\u3002\u4e0b\u9762\u5217\u51fa\u4e86\u76f8\u5173\u7684\u4ee3\u7801\u66f4\u6539\uff1a<\/p>\n<p>private EmployeePayTypeEnum _payType; public EmployeePayTypeEnum PayType<br \/>\n{<br \/>\nget =&gt; _payType;<br \/>\nset =&gt; _payType = value;<br \/>\n}<br \/>\npublic Employee(string name, int id, float pay, string empSsn)<br \/>\n: this(name,0,id,pay, empSsn, EmployeePayTypeEnum.Salaried)<\/p>\n<p>{<br \/>\n}<br \/>\npublic Employee(string name, int age, int id,<br \/>\nfloat pay, string empSsn, EmployeePayTypeEnum payType)<br \/>\n{<br \/>\nName = name;<br \/>\nId = id;<br \/>\nAge = age;<br \/>\nPay = pay;<br \/>\nSocialSecurityNumber = empSsn;<br \/>\nPayType = payType;<br \/>\n}<\/p>\n<p>Now that all of the pieces are in place, the GiveBonus() method can be updated based on the pay type<br \/>\nof the employee. Commissioned employees get 10 percent of the bonus, hourly get the equivalent of 40 hours of the prorated bonus, and salaried get the entered amount. The updated GiveBonus() method is listed here:<br \/>\n\u73b0\u5728\u6240\u6709\u90e8\u5206\u90fd\u5df2\u5c31\u7eea\uff0c\u53ef\u4ee5\u6839\u636e\u652f\u4ed8\u7c7b\u578b\u66f4\u65b0 GiveBonus\uff08\uff09 \u65b9\u6cd5\u3002\u7684\u5458\u5de5\u3002\u59d4\u6258\u5458\u5de5\u83b7\u5f97\u5956\u91d1\u7684 10%\uff0c\u6bcf\u5c0f\u65f6\u83b7\u5f97\u76f8\u5f53\u4e8e 40 \u5c0f\u65f6\u6309\u6bd4\u4f8b\u5206\u914d\u7684\u5956\u91d1\uff0c\u53d7\u85aa\u5458\u5de5\u83b7\u5f97\u8f93\u5165\u7684\u91d1\u989d\u3002\u66f4\u65b0\u540e\u7684 GiveBonus\uff08\uff09 \u65b9\u6cd5\u5982\u4e0b\u6240\u793a\uff1a<\/p>\n<p>public void GiveBonus(float amount)<br \/>\n{<br \/>\nPay = this switch<br \/>\n{<br \/>\n{PayType: EmployeePayTypeEnum.Commission }<br \/>\n=&gt; Pay += .10F <em> amount,<br \/>\n{PayType: EmployeePayTypeEnum.Hourly }<br \/>\n=&gt; Pay += 40F <\/em> amount\/2080F,<br \/>\n{PayType: EmployeePayTypeEnum.Salaried }<br \/>\n=&gt; Pay += amount,<br \/>\n_ =&gt; Pay+=0<br \/>\n};<br \/>\n}<br \/>\nAs with other switch statements that use pattern matching, either there must be a catchall case statement or the switch statement must throw an exception if none of the case statements is met.<br \/>\n\u4e0e\u5176\u4ed6\u4f7f\u7528\u6a21\u5f0f\u5339\u914d\u7684 switch \u8bed\u53e5\u4e00\u6837\uff0c\u8981\u4e48\u5fc5\u987b\u6709\u4e00\u4e2a\u5305\u7f57\u4e07\u8c61\u7684\u60c5\u51b5\u8bed\u53e5\u6216 switch \u8bed\u53e5\u5fc5\u987b\u5f15\u53d1\u5f02\u5e38\uff0c\u5982\u679c\u4e0d\u6ee1\u8db3\u4efb\u4f55 case \u8bed\u53e5\u3002<\/p>\n<p>To test this, add the following code to the top-level statements:<br \/>\n\u82e5\u8981\u5bf9\u6b64\u8fdb\u884c\u6d4b\u8bd5\uff0c\u8bf7\u5c06\u4ee5\u4e0b\u4ee3\u7801\u6dfb\u52a0\u5230\u9876\u7ea7\u8bed\u53e5\u4e2d\uff1a<\/p>\n<p>Employee emp = new Employee(&quot;Marvin&quot;,45,123,1000,&quot;111-11-1111&quot;,EmployeePayTypeEnum. Salaried);<br \/>\nConsole.WriteLine(emp.Pay); emp.GiveBonus(100); Console.WriteLine(emp.Pay);<\/p>\n<p>More than one property can be used in the pattern. Suppose you wanted to make sure each of the employees getting a bonus was older than the age of 18. You can update the method to the following:<br \/>\n\u6a21\u5f0f\u4e2d\u53ef\u4ee5\u4f7f\u7528\u591a\u4e2a\u5c5e\u6027\u3002\u5047\u8bbe\u60a8\u60f3\u786e\u4fdd\u6bcf\u4f4d\u83b7\u5f97\u5956\u91d1\u7684\u5458\u5de5\u90fd\u8d85\u8fc7 18 \u5c81\u3002\u53ef\u4ee5\u5c06\u8be5\u65b9\u6cd5\u66f4\u65b0\u4e3a\u4ee5\u4e0b\u5185\u5bb9\uff1a<\/p>\n<p>public void GiveBonus(float amount)<br \/>\n{<br \/>\nPay = this switch<br \/>\n{<br \/>\n{Age: &gt;= 18, PayType: EmployeePayTypeEnum.Commission }<br \/>\n=&gt; Pay += .10F * amount,<br \/>\n{ Age: &gt;= 18, PayType: EmployeePayTypeEnum.Hourly }<\/p>\n<p>=&gt; Pay += 40F * amount\/2080F,<br \/>\n{ Age: &gt;= 18, PayType: EmployeePayTypeEnum.Salaried }<br \/>\n=&gt; Pay += amount,<br \/>\n_ =&gt; Pay+=0<br \/>\n};<br \/>\n}<br \/>\nProperty patterns can be nested to navigate down the property chain. To demonstrate this, add a public property for the HireDate, like this:<br \/>\n\u53ef\u4ee5\u5d4c\u5957\u5c5e\u6027\u6a21\u5f0f\u4ee5\u6cbf\u5c5e\u6027\u94fe\u5411\u4e0b\u5bfc\u822a\u3002\u82e5\u8981\u6f14\u793a\u8fd9\u4e00\u70b9\uff0c\u8bf7\u6dfb\u52a0\u516c\u5171ireDate \u7684\u5c5e\u6027\uff0c\u5982\u4e0b\u6240\u793a\uff1a<\/p>\n<p>private DateTime _hireDate; public DateTime HireDate<br \/>\n{<br \/>\nget =&gt; _hireDate;<br \/>\nset =&gt; _hireDate = value;<br \/>\n}<br \/>\nNext, update the switch statement to check to make sure each employee\u2019s hire year was after 2020 to qualify for the bonus:<br \/>\n\u63a5\u4e0b\u6765\uff0c\u66f4\u65b0 switch \u8bed\u53e5\u4ee5\u68c0\u67e5\u4ee5\u786e\u4fdd\u6bcf\u4e2a\u5458\u5de5\u7684\u96c7\u7528\u5e74\u4efd\u5728 2020 \u5e74\u4e4b\u540e\u5230\u6709\u8d44\u683c\u83b7\u5f97\u5956\u91d1\uff1a<\/p>\n<p>public void GiveBonus(float amount)<br \/>\n{<br \/>\nPay = this switch<br \/>\n{<br \/>\n{Age: &gt;= 18, PayType: EmployeePayTypeEnum.Commission , HireDate: { Year: &gt; 2020 }}<br \/>\n=&gt; Pay += .10F <em> amount,<br \/>\n{ Age: &gt;= 18, PayType: EmployeePayTypeEnum.Hourly , HireDate: { Year: &gt; 2020 } }<br \/>\n=&gt; Pay += 40F <\/em> amount\/2080F,<br \/>\n{ Age: &gt;= 18, PayType: EmployeePayTypeEnum.Salaried , HireDate: { Year: &gt; 2020 } }<br \/>\n=&gt; Pay += amount,<br \/>\n_ =&gt; Pay+=0<br \/>\n};<br \/>\n}<\/p>\n<h2>Extended Property Patterns (New 10.0)<\/h2>\n<p>\u6269\u5c55\u5c5e\u6027\u6a21\u5f0f\uff08\u65b0 10.0\uff09<\/p>\n<p>New in C# 10, extended property patterns can be used instead of nesting downstream properties. This update cleans up the previous example, as shown here:<br \/>\n\u4f5c\u4e3a C# 10 \u4e2d\u7684\u65b0\u589e\u529f\u80fd\uff0c\u53ef\u4ee5\u4f7f\u7528\u6269\u5c55\u5c5e\u6027\u6a21\u5f0f\u6765\u4ee3\u66ff\u5d4c\u5957\u4e0b\u6e38\u5c5e\u6027\u3002\u6b64\u66f4\u65b0\u6e05\u7406\u4e86\u524d\u9762\u7684\u793a\u4f8b\uff0c\u5982\u4e0b\u6240\u793a\uff1a<\/p>\n<p>public void GiveBonus(float amount)<br \/>\n{<br \/>\nPay = this switch<br \/>\n{<br \/>\n{ Age: &gt;= 18, PayType: EmployeePayTypeEnum.Commission, HireDate.Year: &gt; 2020 }<br \/>\n=&gt; Pay += .10F <em> amount,<br \/>\n{ Age: &gt;= 18, PayType: EmployeePayTypeEnum.Hourly, HireDate.Year: &gt; 2020 }<br \/>\n=&gt; Pay += 40F <\/em> amount \/ 2080F,<br \/>\n{ Age: &gt;= 18, PayType: EmployeePayTypeEnum.Salaried, HireDate.Year: &gt; 2020 }<br \/>\n=&gt; Pay += amount,<br \/>\n_ =&gt; Pay += 0<br \/>\n};<br \/>\n}<\/p>\n<h2>Understanding Automatic Properties<\/h2>\n<p>\u4e86\u89e3\u81ea\u52a8\u5c5e\u6027<\/p>\n<p>When you are building properties to encapsulate your data, it is common to find that the set scopes have code to enforce business rules of your program. However, in some cases, you may not need any implementation logic beyond simply getting and setting the value. This means you can end up with a lot of code looking like the following:<br \/>\n\u5728\u751f\u6210\u5c5e\u6027\u4ee5\u5c01\u88c5\u6570\u636e\u65f6\uff0c\u901a\u5e38\u4f1a\u53d1\u73b0\u8bbe\u7f6e\u7684\u8303\u56f4\u5177\u6709\u5f3a\u5236\u5b9e\u65bd\u7a0b\u5e8f\u4e1a\u52a1\u89c4\u5219\u7684\u4ee3\u7801\u3002\u4f46\u662f\uff0c\u5728\u67d0\u4e9b\u60c5\u51b5\u4e0b\uff0c\u60a8\u53ef\u80fd\u4e0d\u9700\u8981\u4efb\u4f55\u5b9e\u73b0\u903b\u8f91\u4e0d\u4ec5\u4ec5\u662f\u83b7\u53d6\u548c\u8bbe\u7f6e\u503c\u3002\u8fd9\u610f\u5473\u7740\u60a8\u6700\u7ec8\u53ef\u80fd\u4f1a\u5f97\u5230\u5982\u4e0b\u6240\u793a\u7684\u5927\u91cf\u4ee3\u7801\uff1a<\/p>\n<p>\/\/ An Employee Car type using standard property<br \/>\n\/\/ syntax. class Car<br \/>\n{<br \/>\nprivate string carName = &quot;&quot;; public string PetName<br \/>\n{<br \/>\nget { return carName; } set { carName = value; }<br \/>\n}<br \/>\n}<\/p>\n<p>In these cases, it can become rather verbose to define private backing fields and simple property definitions multiple times. By way of an example, if you are modeling a class that requires nine private points of field data, you end up authoring nine related properties that are little more than thin wrappers for encapsulation services.<br \/>\n\u5728\u8fd9\u4e9b\u60c5\u51b5\u4e0b\uff0c\u591a\u6b21\u5b9a\u4e49\u79c1\u6709\u652f\u6301\u5b57\u6bb5\u548c\u7b80\u5355\u5c5e\u6027\u5b9a\u4e49\u53ef\u80fd\u4f1a\u53d8\u5f97\u76f8\u5f53\u5197\u957f\u3002\u4f8b\u5982\uff0c\u5982\u679c\u60a8\u6b63\u5728\u5bf9\u4e00\u4e2a\u9700\u8981\u4e5d\u4e2a\u79c1\u6709\u5b57\u6bb5\u6570\u636e\u7684\u7c7b\u8fdb\u884c\u5efa\u6a21\uff0c\u5219\u6700\u7ec8\u5c06\u521b\u4f5c\u4e5d\u4e2a\u76f8\u5173\u5c5e\u6027\uff0c\u8fd9\u4e9b\u5c5e\u6027\u53ea\u4e0d\u8fc7\u662f\u5c01\u88c5\u670d\u52a1\u7684\u7cbe\u7b80\u5305\u88c5\u5668\u3002<\/p>\n<p>To streamline the process of providing simple encapsulation of field data, you may use automatic property syntax. As the name implies, this feature will offload the work of defining a private backing field and the related C# property member to the compiler using a new bit of syntax. To illustrate, create a new Console Application project named AutoProps and add a new class file named Car.cs. Now, consider this reworking of the Car class, which uses this syntax to quickly create three properties:<br \/>\n\u4e3a\u4e86\u7b80\u5316\u63d0\u4f9b\u5b57\u6bb5\u6570\u636e\u7b80\u5355\u5c01\u88c5\u7684\u8fc7\u7a0b\uff0c\u53ef\u4ee5\u4f7f\u7528\u81ea\u52a8\u5c5e\u6027\u8bed\u6cd5\u3002\u987e\u540d\u601d\u4e49\uff0c\u6b64\u529f\u80fd\u5c06\u4f7f\u7528\u65b0\u8bed\u6cd5\u5c06\u5b9a\u4e49\u79c1\u6709\u652f\u6301\u5b57\u6bb5\u548c\u76f8\u5173 C# \u5c5e\u6027\u6210\u5458\u7684\u5de5\u4f5c\u5378\u8f7d\u5230\u7f16\u8bd1\u5668\u3002\u4e3a\u4e86\u8bf4\u660e\u8fd9\u4e00\u70b9\uff0c\u8bf7\u521b\u5efa\u4e00\u4e2a\u540d\u4e3a AutoProps \u7684\u65b0\u63a7\u5236\u53f0\u5e94\u7528\u7a0b\u5e8f\u9879\u76ee\uff0c\u5e76\u6dfb\u52a0\u4e00\u4e2a\u540d\u4e3a Car.cs \u7684\u65b0\u7c7b\u6587\u4ef6\u3002\u73b0\u5728\uff0c\u8003\u8651\u5bf9 Car \u7c7b\u7684\u91cd\u65b0\u8bbe\u8ba1\uff0c\u5b83\u4f7f\u7528\u6b64\u8bed\u6cd5\u5feb\u901f\u521b\u5efa\u4e09\u4e2a\u5c5e\u6027\uff1a<\/p>\n<p>namespace AutoProps; class Car<br \/>\n{<br \/>\n\/\/ Automatic properties! No need to define backing fields. public string PetName { get; set; }<br \/>\npublic int Speed { get; set; } public string Color { get; set; }<br \/>\n}<\/p>\n<p>\u25a0 Note  Visual studio and Visual studio Code provide the prop code snippet. if you type prop inside a class definition and press the Tab key twice, the ide will generate starter code for a new automatic property. You can then use the Tab key to cycle through each part of the definition to fill in the details. give it a try!<br \/>\n\u6ce8\u610f Visual Studio \u548c Visual Studio Code \u63d0\u4f9b\u4e86 prop \u4ee3\u7801\u7247\u6bb5\u3002\u5982\u679c\u5728\u7c7b\u5b9a\u4e49\u4e2d\u952e\u5165 prop \u5e76\u6309\u4e24\u6b21 Tab \u952e\uff0cIDE \u5c06\u4e3a\u65b0\u7684\u81ea\u52a8\u5c5e\u6027\u751f\u6210\u8d77\u59cb\u4ee3\u7801\u3002\u7136\u540e\uff0c\u53ef\u4ee5\u4f7f\u7528 Tab \u952e\u5faa\u73af\u6d4f\u89c8\u5b9a\u4e49\u7684\u6bcf\u4e2a\u90e8\u5206\u4ee5\u586b\u5199\u8be6\u7ec6\u4fe1\u606f\u3002\u8bd5\u4e00\u8bd5\uff01<\/p>\n<p>When defining automatic properties, you simply specify the access modifier, underlying data type, property name, and empty get\/set scopes. At compile time, your type will be provided with an autogenerated private backing field and a fitting implementation of the get\/set logic.<br \/>\n\u5b9a\u4e49\u81ea\u52a8\u5c5e\u6027\u65f6\uff0c\u53ea\u9700\u6307\u5b9a\u8bbf\u95ee\u4fee\u9970\u7b26\u3001\u57fa\u7840\u6570\u636e\u7c7b\u578b\u3001\u5c5e\u6027\u540d\u79f0\u548c\u7a7a\u7684 get\/set \u8303\u56f4\u3002\u5728\u7f16\u8bd1\u65f6\uff0c\u5c06\u4e3a\u60a8\u7684\u7c7b\u578b\u63d0\u4f9b\u4e00\u4e2a\u81ea\u52a8\u751f\u6210\u7684\u79c1\u6709\u652f\u6301\u5b57\u6bb5\u548c\u4e00\u4e2a\u5408\u9002\u7684 get\/set \u903b\u8f91\u5b9e\u73b0\u3002<\/p>\n<p>\u25a0 Note  The name of the autogenerated private backing field is not visible within your C# code base. The only way to see it is to make use of a tool such as ildasm.exe.<br \/>\n\u6ce8\u610f \u81ea\u52a8\u751f\u6210\u7684\u79c1\u6709\u652f\u6301\u5b57\u6bb5\u7684\u540d\u79f0\u5728 C# \u4ee3\u7801\u5e93\u4e2d\u4e0d\u53ef\u89c1\u3002\u770b\u5230\u5b83\u7684\u552f\u4e00\u65b9\u6cd5\u662f\u4f7f\u7528\u8bf8\u5982ildasm.exe\u4e4b\u7c7b\u7684\u5de5\u5177\u3002<\/p>\n<p>Since C# version 6, it is possible to define a \u201cread-only automatic property\u201d by omitting the set scope. Read-only auto properties can be set only in the constructor. However, it is not possible to define a write- only property. To solidify, consider the following:<br \/>\n\u4ece C# \u7248\u672c 6 \u5f00\u59cb\uff0c\u53ef\u4ee5\u901a\u8fc7\u7701\u7565\u8bbe\u7f6e\u7684\u8303\u56f4\u6765\u5b9a\u4e49\u201c\u53ea\u8bfb\u81ea\u52a8\u5c5e\u6027\u201d\u3002\u53ea\u8bfb\u81ea\u52a8\u5c5e\u6027\u53ea\u80fd\u5728\u6784\u9020\u51fd\u6570\u4e2d\u8bbe\u7f6e\u3002\u4f46\u662f\uff0c\u65e0\u6cd5\u5b9a\u4e49\u53ea\u5199\u5c5e\u6027\u3002\u8981\u5de9\u56fa\uff0c\u8bf7\u8003\u8651\u4ee5\u4e0b\u4e8b\u9879\uff1a<\/p>\n<p>\/\/ Read-only property? This is OK! public int MyReadOnlyProp { get; }<\/p>\n<p>\/\/ Write only property? Error! public int MyWriteOnlyProp { set; }<\/p>\n<h2>Interacting with Automatic Properties<\/h2>\n<p>\u4e0e\u81ea\u52a8\u5c5e\u4e92<\/p>\n<p>Because the compiler will define the private backing field at compile time (and given that these fields are not directly accessible in C# code), the class-defining automatic properties will always need to use property syntax to get and set the underlying value. This is important to note because many programmers make direct use of the private fields within a class definition, which is not possible in this case. For example, if the Car class were to provide a DisplayStats() method, it would need to implement this method using the property name.<br \/>\n\u7531\u4e8e\u7f16\u8bd1\u5668\u5c06\u5728\u7f16\u8bd1\u65f6\u5b9a\u4e49\u79c1\u6709\u652f\u6301\u5b57\u6bb5\uff08\u5e76\u4e14\u9274\u4e8e\u8fd9\u4e9b\u5b57\u6bb5\u5728 C# \u4ee3\u7801\u4e2d\u65e0\u6cd5\u76f4\u63a5\u8bbf\u95ee\uff09\uff0c\u56e0\u6b64\u7c7b\u5b9a\u4e49\u81ea\u52a8\u5c5e\u6027\u5c06\u59cb\u7ec8\u9700\u8981\u4f7f\u7528\u5c5e\u6027\u8bed\u6cd5\u6765\u83b7\u53d6\u548c\u8bbe\u7f6e\u57fa\u7840\u503c\u3002\u8fd9\u4e00\u70b9\u5f88\u91cd\u8981\uff0c\u56e0\u4e3a\u8bb8\u591a\u7a0b\u5e8f\u5458\u76f4\u63a5\u4f7f\u7528\u7c7b\u5b9a\u4e49\u4e2d\u7684\u79c1\u6709\u5b57\u6bb5\uff0c\u8fd9\u5728\u8fd9\u79cd\u60c5\u51b5\u4e0b\u662f\u4e0d\u53ef\u80fd\u7684\u3002\u4f8b\u5982\uff0c\u5982\u679c Car \u7c7b\u8981\u63d0\u4f9b DisplayStats\uff08\uff09 \u65b9\u6cd5\uff0c\u5219\u9700\u8981\u4f7f\u7528\u5c5e\u6027\u540d\u79f0\u5b9e\u73b0\u6b64\u65b9\u6cd5\u3002<\/p>\n<p>class Car<br \/>\n{<br \/>\n\/\/ Automatic properties!<br \/>\npublic string PetName { get; set; } public int Speed { get; set; } public string Color { get; set; }<\/p>\n<p>public void DisplayStats()<br \/>\n{<br \/>\nConsole.WriteLine(&quot;Car Name: {0}&quot;, PetName); Console.WriteLine(&quot;Speed: {0}&quot;, Speed);<br \/>\nConsole.WriteLine(&quot;Color: {0}&quot;, Color);<br \/>\n}<br \/>\n}<\/p>\n<p>When you are using an object defined with automatic properties, you will be able to assign and obtain the values using the expected property syntax.<br \/>\n\u4f7f\u7528\u901a\u8fc7\u81ea\u52a8\u5c5e\u6027\u5b9a\u4e49\u7684\u5bf9\u8c61\u65f6\uff0c\u60a8\u5c06\u80fd\u591f\u4f7f\u7528\u9884\u671f\u7684\u5c5e\u6027\u8bed\u6cd5\u5206\u914d\u548c\u83b7\u53d6\u503c\u3002<\/p>\n<p>using AutoProps;<br \/>\nConsole.WriteLine(&quot;<strong><strong><em> Fun with Automatic Properties <\/em><\/strong><\/strong>\\n&quot;); Car c = new Car();<br \/>\nc.PetName = &quot;Frank&quot;; c.Speed = 55; c.Color = &quot;Red&quot;;<\/p>\n<p>Console.WriteLine(&quot;Your car is named {0}? That's odd...&quot;, c.PetName);<br \/>\nc.DisplayStats();<\/p>\n<p>Console.ReadLine();<\/p>\n<h2>Automatic Properties and Default Values<\/h2>\n<p>\u81ea\u52a8\u5c5e\u6027\u548c\u9ed8\u8ba4\u503c<\/p>\n<p>When you use automatic properties to encapsulate numerical or Boolean data, you are able to use the autogenerated type properties straightaway within your code base, as the hidden backing fields will be assigned a safe default value (false for Booleans and 0 for numerical data). However, be aware that if you use automatic property syntax to wrap another class variable, the hidden private reference type will also be set to a default value of null (which can prove problematic if you are not careful).<br \/>\n\u4f7f\u7528\u81ea\u52a8\u5c5e\u6027\u5c01\u88c5\u6570\u503c\u6216\u5e03\u5c14\u6570\u636e\u65f6\uff0c\u53ef\u4ee5\u76f4\u63a5\u5728\u4ee3\u7801\u5e93\u4e2d\u4f7f\u7528\u81ea\u52a8\u751f\u6210\u7684\u7c7b\u578b\u5c5e\u6027\uff0c\u56e0\u4e3a\u9690\u85cf\u7684\u652f\u6301\u5b57\u6bb5\u5c06\u88ab\u5206\u914d\u4e00\u4e2a\u5b89\u5168\u7684\u9ed8\u8ba4\u503c\uff08\u5bf9\u4e8e\u5e03\u5c14\u503c\u4e3a false\uff0c\u5bf9\u4e8e\u6570\u503c\u6570\u636e\u4e3a 0\uff09\u3002\u4f46\u662f\uff0c\u8bf7\u6ce8\u610f\uff0c\u5982\u679c\u4f7f\u7528\u81ea\u52a8\u5c5e\u6027\u8bed\u6cd5\u5305\u88c5\u53e6\u4e00\u4e2a\u7c7b\u53d8\u91cf\uff0c\u5219\u9690\u85cf\u7684\u79c1\u6709\u5f15\u7528\u7c7b\u578b\u4e5f\u5c06\u8bbe\u7f6e\u4e3a\u9ed8\u8ba4\u503c null\uff08\u5982\u679c\u4e0d\u5c0f\u5fc3\uff0c\u8fd9\u53ef\u80fd\u4f1a\u6709\u95ee\u9898\uff09\u3002<\/p>\n<p>Let\u2019s insert into your current project a new class file named Garage.cs, which makes use of two automatic properties (of course, a real garage class might maintain a collection of Car objects; however,<br \/>\nignore that detail here).<br \/>\n\u8ba9\u6211\u4eec\u5728\u5f53\u524d\u9879\u76ee\u4e2d\u63d2\u5165\u4e00\u4e2a\u540d\u4e3a Garage.cs \u7684\u65b0\u7c7b\u6587\u4ef6\uff0c\u8be5\u6587\u4ef6\u4f7f\u7528\u4e24\u4e2a\u81ea\u52a8\u5c5e\u6027\uff08\u5f53\u7136\uff0c\u771f\u6b63\u7684 garage \u7c7b\u53ef\u80fd\u4f1a\u7ef4\u62a4 Car \u5bf9\u8c61\u7684\u96c6\u5408;\u4f46\u662f\uff0c\u5ffd\u7565\u6b64\u5904\u7684\u7ec6\u8282\uff09\u3002<\/p>\n<p>namespace AutoProps; class Garage<br \/>\n{<br \/>\n\/\/ The hidden int backing field is set to zero! public int NumberOfCars { get; set; }<\/p>\n<p>\/\/ The hidden Car backing field is set to null! public Car MyAuto { get; set; }<br \/>\n}<\/p>\n<p>Given C#\u2019s default values for field data, you would be able to print out the value of NumberOfCars as is (as it is automatically assigned the value of zero), but if you directly invoke MyAuto, you will receive a \u201cnull reference exception\u201d at runtime, as the Car member variable used in the background has not been assigned to a new object.<br \/>\n\u7ed9\u5b9a C# \u5b57\u6bb5\u6570\u636e\u7684\u9ed8\u8ba4\u503c\uff0c\u60a8\u5c06\u80fd\u591f\u6309\u539f\u6837\u6253\u5370\u51fa NumberOfCars \u7684\u503c\uff08\u56e0\u4e3a\u5b83\u4f1a\u81ea\u52a8\u5206\u914d\u503c\u96f6\uff09\uff0c\u4f46\u5982\u679c\u76f4\u63a5\u8c03\u7528 MyAuto\uff0c\u60a8\u5c06\u5728\u8fd0\u884c\u65f6\u6536\u5230\u201c\u7a7a\u5f15\u7528\u5f02\u5e38\u201d\uff0c\u56e0\u4e3a\u540e\u53f0\u4f7f\u7528\u7684 Car \u6210\u5458\u53d8\u91cf\u5c1a\u672a\u5206\u914d\u7ed9\u65b0\u5bf9\u8c61\u3002<br \/>\n...<br \/>\nGarage g = new Garage();<\/p>\n<p>\/\/ OK, prints default value of zero. Console.WriteLine(&quot;Number of Cars: {0}&quot;, g.NumberOfCars);<\/p>\n<p>\/\/ Runtime error! Backing field is currently null! Console.WriteLine(g.MyAuto.PetName); Console.ReadLine();<\/p>\n<p>To solve this problem, you could update the class constructors to ensure the object comes to life in a safe manner. Here is an example:<br \/>\n\u82e5\u8981\u89e3\u51b3\u6b64\u95ee\u9898\uff0c\u53ef\u4ee5\u66f4\u65b0\u7c7b\u6784\u9020\u51fd\u6570\uff0c\u4ee5\u786e\u4fdd\u5bf9\u8c61\u4ee5\u5b89\u5168\u7684\u65b9\u5f0f\u751f\u6210\u3002\u4e0b\u9762\u662f\u4e00\u4e2a\u793a\u4f8b\uff1a<\/p>\n<p>class Garage<br \/>\n{<br \/>\n\/\/ The hidden backing field is set to zero! public int NumberOfCars { get; set; }<br \/>\n\/\/ The hidden backing field is set to null! public Car MyAuto { get; set; }<br \/>\n\/\/ Must use constructors to override default<br \/>\n\/\/ values assigned to hidden backing fields. public Garage()<br \/>\n{<br \/>\nMyAuto = new Car();<br \/>\nNumberOfCars = 1;<br \/>\n}<\/p>\n<p>public Garage(Car car, int number)<br \/>\n{<br \/>\nMyAuto = car;<br \/>\nNumberOfCars = number;<br \/>\n}<br \/>\n}<\/p>\n<p>With this modification, you can now place a Car object into the Garage object as so:<br \/>\n\u901a\u8fc7\u6b64\u4fee\u6539\uff0c\u60a8\u73b0\u5728\u53ef\u4ee5\u5c06 Car \u5bf9\u8c61\u653e\u5165\u8f66\u5e93\u5bf9\u8c61\u4e2d\uff0c\u5982\u4e0b\u6240\u793a\uff1a<\/p>\n<p>using AutoProps;<\/p>\n<p>Console.WriteLine(&quot;<strong><strong><em> Fun with Automatic Properties <\/em><\/strong><\/strong>\\n&quot;);<\/p>\n<p>\/\/ Make a car.<br \/>\nCar c = new Car(); c.PetName = &quot;Frank&quot;; c.Speed = 55; c.Color = &quot;Red&quot;; c.DisplayStats();<\/p>\n<p>\/\/ Put car in the garage. Garage g = new Garage(); g.MyAuto = c;<br \/>\nConsole.WriteLine(&quot;Number of Cars in garage: {0}&quot;, g.NumberOfCars); Console.WriteLine(&quot;Your car is named: {0}&quot;, g.MyAuto.PetName);<\/p>\n<p>Console.ReadLine();<\/p>\n<h2>Initializing Automatic Properties<\/h2>\n<p>\u521d\u59cb\u5316\u81ea\u52a8\u5c5e\u6027<\/p>\n<p>While the previous approach works, since the release of C# 6, you are provided with a language feature that can simplify how an automatic property receives its initial value assignment. Recall from the onset of this chapter, a data field of a class can be directly assigned an initial value upon declaration. Here is an example:<br \/>\n\u867d\u7136\u524d\u9762\u7684\u65b9\u6cd5\u6709\u6548\uff0c\u4f46\u81ea C# 6 \u53d1\u5e03\u4ee5\u6765\uff0c\u4e3a\u60a8\u63d0\u4f9b\u4e86\u4e00\u79cd\u8bed\u8a00\u529f\u80fd\uff0c\u53ef\u4ee5\u7b80\u5316\u81ea\u52a8\u5c5e\u6027\u63a5\u6536\u5176\u521d\u59cb\u503c\u8d4b\u503c\u7684\u65b9\u5f0f\u3002\u56de\u60f3\u4e00\u4e0b\uff0c\u4ece\u672c\u7ae0\u4e00\u5f00\u59cb\uff0c\u7c7b\u7684\u6570\u636e\u5b57\u6bb5\u53ef\u4ee5\u5728\u58f0\u660e\u65f6\u76f4\u63a5\u8d4b\u503c\u3002\u4e0b\u9762\u662f\u4e00\u4e2a\u793a\u4f8b\uff1a<\/p>\n<p>class Car<br \/>\n{<br \/>\nprivate int numberOfDoors = 2;<br \/>\n}<\/p>\n<p>In a similar manner, C# now allows you to assign an initial value to the underlying backing field generated by the compiler. This alleviates you from the hassle of adding code statements in class constructors to ensure property data comes to life as intended.<\/p>\n<p>\u4ee5\u7c7b\u4f3c\u7684\u65b9\u5f0f\uff0cC# \u73b0\u5728\u5141\u8bb8\u60a8\u5c06\u521d\u59cb\u503c\u5206\u914d\u7ed9\u7f16\u8bd1\u5668\u751f\u6210\u7684\u57fa\u7840\u652f\u6301\u5b57\u6bb5\u3002\u8fd9\u51cf\u8f7b\u4e86\u5728\u7c7b\u6784\u9020\u51fd\u6570\u4e2d\u6dfb\u52a0\u4ee3\u7801\u8bed\u53e5\u4ee5\u786e\u4fdd\u5c5e\u6027\u6570\u636e\u6309\u9884\u671f\u8fdb\u884c\u751f\u6d3b\u7684\u9ebb\u70e6\u3002<\/p>\n<p>Here is an updated version of the Garage class that is initializing automatic properties to fitting values.<br \/>\n\u4e0b\u9762\u662f Garage \u7c7b\u7684\u66f4\u65b0\u7248\u672c\uff0c\u8be5\u7c7b\u5c06\u81ea\u52a8\u5c5e\u6027\u521d\u59cb\u5316\u4e3a\u9002\u5408\u503c\u3002<\/p>\n<p>Note you no longer need to add logic to your default class constructor to make safe assignments. In this iteration, you are directly assigning a new Car object to the MyAuto property.<br \/>\n\u8bf7\u6ce8\u610f\uff0c\u60a8\u4e0d\u518d\u9700\u8981\u5411\u9ed8\u8ba4\u7c7b\u6784\u9020\u51fd\u6570\u6dfb\u52a0\u903b\u8f91\u6765\u8fdb\u884c\u5b89\u5168\u8d4b\u503c\u3002\u5728\u6b64\u8fed\u4ee3\u4e2d\uff0c\u60a8\u5c06\u76f4\u63a5\u5c06\u65b0\u7684 Car \u5bf9\u8c61\u5206\u914d\u7ed9 MyAuto \u5c5e\u6027\u3002<\/p>\n<p>class Garage<br \/>\n{<br \/>\n\/\/ The hidden backing field is set to 1. public int NumberOfCars { get; set; } = 1;<\/p>\n<p>\/\/ The hidden backing field is set to a new Car object. public Car MyAuto { get; set; } = new Car();<\/p>\n<p>public Garage(){}<br \/>\npublic Garage(Car car, int number)<br \/>\n{<br \/>\nMyAuto = car;<br \/>\nNumberOfCars = number;<br \/>\n}<br \/>\n}<\/p>\n<p>As you may agree, automatic properties are a nice feature of the C# programming language, as you can define a number of properties for a class using a streamlined syntax. Be aware of course that if you are building a property that requires additional code beyond getting and setting the underlying private field (such as data validation logic, writing to an event log, communicating with a database, etc.), you will be required to define a \u201cnormal\u201d .NET Core property type by hand. C# automatic properties never do more than provide simple encapsulation for an underlying piece of (compiler-generated) private data.<br \/>\n\u60a8\u53ef\u80fd\u540c\u610f\uff0c\u81ea\u52a8\u5c5e\u6027\u662f C# \u7f16\u7a0b\u8bed\u8a00\u7684\u4e00\u4e2a\u5f88\u597d\u7684\u529f\u80fd\uff0c\u56e0\u4e3a\u60a8\u53ef\u4ee5\u4f7f\u7528\u7b80\u5316\u7684\u8bed\u6cd5\u4e3a\u7c7b\u5b9a\u4e49\u8bb8\u591a\u5c5e\u6027\u3002\u5f53\u7136\u8bf7\u6ce8\u610f\uff0c\u5982\u679c\u60a8\u6b63\u5728\u6784\u5efa\u7684\u5c5e\u6027\u9664\u4e86\u83b7\u53d6\u548c\u8bbe\u7f6e\u57fa\u7840\u79c1\u6709\u5b57\u6bb5\uff08\u4f8b\u5982\u6570\u636e\u9a8c\u8bc1\u903b\u8f91\u3001\u5199\u5165\u4e8b\u4ef6\u65e5\u5fd7\u3001\u4e0e\u6570\u636e\u5e93\u901a\u4fe1\u7b49\uff09\u4e4b\u5916\u8fd8\u9700\u8981\u5176\u4ed6\u4ee3\u7801\uff0c\u60a8\u5c06\u9700\u8981\u624b\u52a8\u5b9a\u4e49\u201c\u666e\u901a\u201d.NET Core \u5c5e\u6027\u7c7b\u578b\u3002C# \u81ea\u52a8\u5c5e\u6027\u6240\u505a\u7684\u53ea\u662f\u4e3a\u57fa\u7840\u90e8\u5206\uff08\u7f16\u8bd1\u5668\u751f\u6210\u7684\uff09\u79c1\u6709\u6570\u636e\u63d0\u4f9b\u7b80\u5355\u7684\u5c01\u88c5\u3002<\/p>\n<h2>Understanding Object Initialization<\/h2>\n<p>\u4e86\u89e3\u5bf9\u8c61\u521d\u59cb\u5316<br \/>\nAs shown throughout this chapter, a constructor allows you to specify startup values when creating a new object. On a related note, properties allow you to get and set underlying data in a safe manner. When you are working with other people\u2019s classes, including the classes found within the .NET Core base class library, it is not too uncommon to discover that there is not a single constructor that allows you to set every piece of underlying state data. Given this point, a programmer is typically forced to pick the best constructor possible, after which the programmer makes assignments using a handful of provided properties.<br \/>\n\u5982\u672c\u7ae0\u6240\u793a\uff0c\u6784\u9020\u51fd\u6570\u5141\u8bb8\u60a8\u5728\u521b\u5efa\u65b0\u5bf9\u8c61\u65f6\u6307\u5b9a\u542f\u52a8\u503c\u3002\u5728\u76f8\u5173\u8bf4\u660e\u4e2d\uff0c\u5c5e\u6027\u5141\u8bb8\u60a8\u4ee5\u5b89\u5168\u7684\u65b9\u5f0f\u83b7\u53d6\u548c\u8bbe\u7f6e\u57fa\u7840\u6570\u636e\u3002\u5f53\u60a8\u4f7f\u7528\u5176\u4ed6\u4eba\u7684\u7c7b\uff08\u5305\u62ec\u5728 .NET Core \u57fa\u7c7b\u5e93\u4e2d\u627e\u5230\u7684\u7c7b\uff09\u65f6\uff0c\u53d1\u73b0\u6ca1\u6709\u4e00\u4e2a\u6784\u9020\u51fd\u6570\u5141\u8bb8\u60a8\u8bbe\u7f6e\u6bcf\u4e2a\u90e8\u5206\u7684\u60c5\u51b5\u5e76\u4e0d\u5c11\u89c1\u7684\u57fa\u7840\u72b6\u6001\u6570\u636e\u3002\u9274\u4e8e\u8fd9\u4e00\u70b9\uff0c\u7a0b\u5e8f\u5458\u901a\u5e38\u88ab\u8feb\u9009\u62e9\u6700\u597d\u7684\u6784\u9020\u51fd\u6570\uff0c\u4e4b\u540e\u7a0b\u5e8f\u5458\u4f7f\u7528\u5c11\u6570\u63d0\u4f9b\u7684\u5c5e\u6027\u8fdb\u884c\u8d4b\u503c\u3002<\/p>\n<h2>Looking at the Object Initialization Syntax<\/h2>\n<p>\u67e5\u770b\u5bf9\u8c61\u521d\u59cb\u5316\u8bed\u6cd5<\/p>\n<p>To help streamline the process of getting an object up and running, C# offers object initializer syntax. Using this technique, it is possible to create a new object variable and assign a slew of properties and\/or public fields in a few lines of code. Syntactically, an object initializer consists of a comma-delimited list of specified values, enclosed by the { and } tokens. Each member in the initialization list maps to the name of a public field or public property of the object being initialized.<br \/>\n\u4e3a\u4e86\u5e2e\u52a9\u7b80\u5316\u542f\u52a8\u548c\u8fd0\u884c\u5bf9\u8c61\u7684\u8fc7\u7a0b\uff0cC# \u63d0\u4f9b\u4e86\u5bf9\u8c61\u521d\u59cb\u503c\u8bbe\u5b9a\u9879\u8bed\u6cd5\u3002\u4f7f\u7528\u8fd9\u79cd\u6280\u672f\uff0c\u53ef\u4ee5\u521b\u5efa\u4e00\u4e2a\u65b0\u7684\u5bf9\u8c61\u53d8\u91cf\uff0c\u5e76\u5728\u51e0\u884c\u4ee3\u7801\u4e2d\u5206\u914d\u5927\u91cf\u5c5e\u6027\u548c\/\u6216\u516c\u5171\u5b57\u6bb5\u3002\u4ece\u8bed\u6cd5\u4e0a\u8bb2\uff0c\u5bf9\u8c61\u521d\u59cb\u503c\u8bbe\u5b9a\u9879\u7531\u9017\u53f7\u5206\u9694\u7684\u6307\u5b9a\u503c\u5217\u8868\u7ec4\u6210\uff0c\u7531 { \u548c } \u6807\u8bb0\u62ec\u8d77\u6765\u3002\u521d\u59cb\u5316\u5217\u8868\u4e2d\u7684\u6bcf\u4e2a\u6210\u5458\u90fd\u6620\u5c04\u5230\u8981\u521d\u59cb\u5316\u7684\u5bf9\u8c61\u7684\u516c\u5171\u5b57\u6bb5\u6216\u516c\u5171\u5c5e\u6027\u7684\u540d\u79f0\u3002<br \/>\nTo see this syntax in action, create a new Console Application project named ObjectInitializers. Now, consider a simple class named Point, created using automatic properties (which is not mandatory for object initialization syntax but helps you write some concise code).<br \/>\n\u82e5\u8981\u67e5\u770b\u6b64\u8bed\u6cd5\u7684\u5b9e\u9645\u6548\u679c\uff0c\u8bf7\u521b\u5efa\u4e00\u4e2a\u540d\u4e3a ObjectInitializers \u7684\u65b0\u63a7\u5236\u53f0\u5e94\u7528\u7a0b\u5e8f\u9879\u76ee\u3002\u73b0\u5728\uff0c\u8003\u8651\u4e00\u4e2a\u540d\u4e3a Point \u7684\u7b80\u5355\u7c7b\uff0c\u5b83\u4f7f\u7528\u81ea\u52a8\u5c5e\u6027\u521b\u5efa\uff08\u5bf9\u4e8e\u5bf9\u8c61\u521d\u59cb\u5316\u8bed\u6cd5\u4e0d\u662f\u5fc5\u9700\u7684\uff0c\u4f46\u53ef\u4ee5\u5e2e\u52a9\u60a8\u7f16\u5199\u4e00\u4e9b\u7b80\u6d01\u7684\u4ee3\u7801\uff09\u3002<\/p>\n<p>class Point<br \/>\n{<br \/>\npublic int X { get; set; } public int Y { get; set; }<\/p>\n<p>public Point(int xVal, int yVal)<br \/>\n{<br \/>\nX = xVal;<br \/>\nY = yVal;<br \/>\n}<br \/>\npublic Point() { }<br \/>\npublic void DisplayStats()<\/p>\n<p>{<br \/>\nConsole.WriteLine(&quot;[{0}, {1}]&quot;, X, Y);<br \/>\n}<br \/>\n}<\/p>\n<p>Now consider how you can make Point objects using any of the following approaches:<br \/>\n\u73b0\u5728\u8003\u8651\u5982\u4f55\u4f7f\u7528\u4ee5\u4e0b\u4efb\u4e00\u65b9\u6cd5\u521b\u5efa Point \u5bf9\u8c61\uff1a<\/p>\n<p>using ObjectInitializers;<\/p>\n<p>Console.WriteLine(&quot;<strong><strong><em> Fun with Object Init Syntax <\/em><\/strong><\/strong>\\n&quot;);<\/p>\n<p>\/\/ Make a Point by setting each property manually. Point firstPoint = new Point();<br \/>\nfirstPoint.X = 10;<br \/>\nfirstPoint.Y = 10; firstPoint.DisplayStats();<\/p>\n<p>\/\/ Or make a Point via a custom constructor. Point anotherPoint = new Point(20, 20); anotherPoint.DisplayStats();<\/p>\n<p>\/\/ Or make a Point using object init syntax. Point finalPoint = new Point { X = 30, Y = 30 }; finalPoint.DisplayStats();<br \/>\nConsole.ReadLine();<\/p>\n<p>The final Point variable is not making use of a custom constructor (as one might do traditionally) but is rather setting values to the public X and Y properties. Behind the scenes, the type\u2019s default constructor is invoked, followed by setting the values to the specified properties. To this end, object initialization syntax is just shorthand notation for the syntax used to create a class variable using a default constructor and to set the state data property by property.<br \/>\n\u6700\u540e\u4e00\u4e2a Point \u53d8\u91cf\u4e0d\u4f7f\u7528\u81ea\u5b9a\u4e49\u6784\u9020\u51fd\u6570\uff08\u4f20\u7edf\u4e0a\u53ef\u80fd\u4f1a\u8fd9\u6837\u505a\uff09\uff0c\u800c\u662f\u5c06\u503c\u8bbe\u7f6e\u4e3a\u516c\u5171 X \u548c Y \u5c5e\u6027\u3002\u5728\u540e\u53f0\uff0c\u8c03\u7528\u7c7b\u578b\u7684\u9ed8\u8ba4\u6784\u9020\u51fd\u6570\uff0c\u7136\u540e\u5c06\u503c\u8bbe\u7f6e\u4e3a\u6307\u5b9a\u7684\u5c5e\u6027\u3002\u4e3a\u6b64\uff0c\u5bf9\u8c61\u521d\u59cb\u5316\u8bed\u6cd5\u53ea\u662f\u7528\u4e8e\u4f7f\u7528\u9ed8\u8ba4\u6784\u9020\u51fd\u6570\u521b\u5efa\u7c7b\u53d8\u91cf\u548c\u9010\u4e2a\u5c5e\u6027\u8bbe\u7f6e\u72b6\u6001\u6570\u636e\u5c5e\u6027\u7684\u8bed\u6cd5\u7684\u7b80\u5199\u8868\u793a\u6cd5\u3002<\/p>\n<p>\u25a0 Note  it\u2019s important to remember that the object initialization process is using the property setter implicitly. if the property setter is marked private, this syntax cannot be used.<br \/>\n\u8bf7\u6ce8\u610f\uff0c\u8bf7\u52a1\u5fc5\u8bb0\u4f4f\uff0c\u5bf9\u8c61\u521d\u59cb\u5316\u8fc7\u7a0b\u9690\u5f0f\u4f7f\u7528\u5c5e\u6027 setter\u3002\u5982\u679c\u5c5e\u6027\u8d44\u6e90\u5e93\u6807\u8bb0\u4e3a\u79c1\u6709\uff0c\u5219\u4e0d\u80fd\u4f7f\u7528\u6b64\u8bed\u6cd5\u3002<\/p>\n<h2>Using init-Only Setters (New 9.0)<\/h2>\n<p>\u4f7f\u7528\u4ec5\u521d\u59cb\u5316\u8bbe\u7f6e\u5668\uff08\u65b0\u7248 9.0\uff09<\/p>\n<p>A new feature added in C# 9.0 is init-only setters. These setters enable a property to have its value set during initialization, but after construction is complete on the object, the property becomes read-only. These types of properties are call immutable. Add a new class file named ReadOnlyPointAfterCreation.cs to your project, and add the following code:<br \/>\nC# 9.0 \u4e2d\u6dfb\u52a0\u7684\u4e00\u9879\u65b0\u529f\u80fd\u662f\u4ec5\u521d\u59cb\u5316\u8d44\u6e90\u5e93\u3002\u8fd9\u4e9b\u8d44\u6e90\u5e93\u4f7f\u5c5e\u6027\u80fd\u591f\u5728\u521d\u59cb\u5316\u671f\u95f4\u8bbe\u7f6e\u5176\u503c\uff0c\u4f46\u5728\u5bf9\u8c61\u4e0a\u5b8c\u6210\u6784\u9020\u540e\uff0c\u8be5\u5c5e\u6027\u5c06\u53d8\u4e3a\u53ea\u8bfb\u3002\u8fd9\u4e9b\u7c7b\u578b\u7684\u5c5e\u6027\u79f0\u4e3a\u4e0d\u53ef\u53d8\u3002 \u5c06\u4e00\u4e2a\u540d\u4e3a ReadOnlyPointAfterCreation.cs \u7684\u65b0\u7c7b\u6587\u4ef6\u6dfb\u52a0\u5230\u9879\u76ee\u4e2d\uff0c\u5e76\u6dfb\u52a0\u4ee5\u4e0b\u4ee3\u7801\uff1a<\/p>\n<p>namespace ObjectInitializers; class PointReadOnlyAfterCreation<br \/>\n{<br \/>\npublic int X { get; init; } public int Y { get; init; }<\/p>\n<p>public void DisplayStats()<br \/>\n{<br \/>\nConsole.WriteLine(&quot;InitOnlySetter: [{0}, {1}]&quot;, X, Y);<br \/>\n}<br \/>\npublic PointReadOnlyAfterCreation(int xVal, int yVal)<br \/>\n{<br \/>\nX = xVal;<br \/>\nY = yVal;<br \/>\n}<br \/>\npublic PointReadOnlyAfterCreation() { }<br \/>\n}<\/p>\n<p>Use the following code to take this new class for a test-drive:<br \/>\n\u4f7f\u7528\u4ee5\u4e0b\u4ee3\u7801\u5c06\u6b64\u65b0\u7c7b\u7528\u4e8e\u4f53\u9a8c\u7248\uff1a<\/p>\n<p>\/\/Make readonly point after construction<br \/>\nPointReadOnlyAfterCreation firstReadonlyPoint = new PointReadOnlyAfterCreation(20, 20); firstReadonlyPoint.DisplayStats();<\/p>\n<p>\/\/ Or make a Point using object init syntax.<br \/>\nPointReadOnlyAfterCreation secondReadonlyPoint = new PointReadOnlyAfterCreation { X = 30, Y = 30 };<br \/>\nsecondReadonlyPoint.DisplayStats();<\/p>\n<p>Notice nothing has changed from the code that you wrote for the Point class, except of course the class name. The difference is that the values for X or Y cannot be modified once the class is created. For example, the following code will not compile:<br \/>\n\u8bf7\u6ce8\u610f\uff0c\u60a8\u4e3a Point \u7c7b\u7f16\u5199\u7684\u4ee3\u7801\u6ca1\u6709\u4efb\u4f55\u53d8\u5316\uff0c\u5f53\u7136\u7c7b\u540d\u9664\u5916\u3002\u4e0d\u540c\u4e4b\u5904\u5728\u4e8e\uff0c\u521b\u5efa\u7c7b\u540e\uff0c\u65e0\u6cd5\u4fee\u6539 X \u6216 Y \u7684\u503c\u3002\u4f8b\u5982\uff0c\u4ee5\u4e0b\u4ee3\u7801\u5c06\u65e0\u6cd5\u7f16\u8bd1\uff1a<\/p>\n<p>\/\/The next two lines will not compile secondReadonlyPoint.X = 10;<br \/>\nsecondReadonlyPoint.Y = 10;<\/p>\n<h2>Calling Custom Constructors with Initialization Syntax<\/h2>\n<p>\u4f7f\u7528\u521d\u59cb\u5316\u8bed\u6cd5\u8c03\u7528\u81ea\u5b9a\u4e49\u6784\u9020\u51fd\u6570<\/p>\n<p>The previous examples initialized Point types by implicitly calling the default constructor on the type.<br \/>\n\u524d\u9762\u7684\u793a\u4f8b\u901a\u8fc7\u9690\u5f0f\u8c03\u7528\u7c7b\u578b\u7684\u9ed8\u8ba4\u6784\u9020\u51fd\u6570\u6765\u521d\u59cb\u5316 Point \u7c7b\u578b\u3002<\/p>\n<p>\/\/ Here, the default constructor is called implicitly. Point finalPoint = new Point { X = 30, Y = 30 };<\/p>\n<p>If you want to be clear about this, it is permissible to explicitly call the default constructor as follows:<br \/>\n\u5982\u679c\u8981\u660e\u786e\u8fd9\u4e00\u70b9\uff0c\u53ef\u4ee5\u663e\u5f0f\u8c03\u7528\u9ed8\u8ba4\u6784\u9020\u51fd\u6570\uff0c\u5982\u4e0b\u6240\u793a\uff1a<\/p>\n<p>\/\/ Here, the default constructor is called explicitly. Point finalPoint = new Point() { X = 30, Y = 30 };<\/p>\n<p>Be aware that when you are constructing a type using initialization syntax, you are able to invoke any constructor defined by the class. Your Point type currently defines a two-argument constructor to set the (x, y) position. Therefore, the following Point declaration results in an X value of 100 and a Y value of 100, regardless of the fact that the constructor arguments specified the values 10 and 16:<br \/>\n\u8bf7\u6ce8\u610f\uff0c\u4f7f\u7528\u521d\u59cb\u5316\u8bed\u6cd5\u6784\u9020\u7c7b\u578b\u65f6\uff0c\u53ef\u4ee5\u8c03\u7528\u8be5\u7c7b\u5b9a\u4e49\u7684\u4efb\u4f55\u6784\u9020\u51fd\u6570\u3002Point \u7c7b\u578b\u5f53\u524d\u5b9a\u4e49\u4e86\u4e00\u4e2a\u53cc\u53c2\u6570\u6784\u9020\u51fd\u6570\u6765\u8bbe\u7f6e \uff08x\uff0c y\uff09 \u4f4d\u7f6e\u3002\u56e0\u6b64\uff0c\u4ee5\u4e0b Point \u58f0\u660e\u5c06\u5bfc\u81f4 X \u503c\u4e3a 100\uff0cY \u503c\u4e3a 100\uff0c\u800c\u4e0d\u8003\u8651\u6784\u9020\u51fd\u6570\u53c2\u6570\u6307\u5b9a\u503c 10 \u548c 16 \u7684\u4e8b\u5b9e\uff1a<\/p>\n<p>\/\/ Calling a custom constructor.<br \/>\nPoint pt = new Point(10, 16) { X = 100, Y = 100 };<\/p>\n<p>Given the current definition of your Point type, calling the custom constructor while using initialization syntax is not terribly useful (and more than a bit verbose). However, if your Point type provides a new constructor that allows the caller to establish a color (via a custom enum named PointColor), the combination of custom constructors and object initialization syntax becomes clear.<br \/>\n\u9274\u4e8e Point \u7c7b\u578b\u7684\u5f53\u524d\u5b9a\u4e49\uff0c\u5728\u4f7f\u7528\u521d\u59cb\u5316\u8bed\u6cd5\u65f6\u8c03\u7528\u81ea\u5b9a\u4e49\u6784\u9020\u51fd\u6570\u5e76\u4e0d\u662f\u975e\u5e38\u6709\u7528\uff08\u800c\u4e14\u6709\u70b9\u5197\u957f\uff09\u3002\u4f46\u662f\uff0c\u5982\u679c\u60a8\u7684\u70b9\u7c7b\u578b\u63d0\u4f9b\u5141\u8bb8\u8c03\u7528\u65b9\u5efa\u7acb\u989c\u8272\u7684\u65b0\u6784\u9020\u51fd\u6570\uff08\u901a\u8fc7\u540d\u4e3a PointColor \u7684\u81ea\u5b9a\u4e49\u679a\u4e3e\uff09\uff0c\u81ea\u5b9a\u4e49\u6784\u9020\u51fd\u6570\u548c\u5bf9\u8c61\u521d\u59cb\u5316\u8bed\u6cd5\u7684\u7ec4\u5408\u53d8\u5f97\u6e05\u6670\u3002<\/p>\n<p>Add a new class named PointColorEnum.cs to your project, and add the following code to create an enum for the color:<br \/>\n\u5c06\u4e00\u4e2a\u540d\u4e3a PointColorEnum.cs \u7684\u65b0\u7c7b\u6dfb\u52a0\u5230\u9879\u76ee\u4e2d\uff0c\u5e76\u6dfb\u52a0\u4ee5\u4e0b\u4ee3\u7801\u4ee5\u521b\u5efa\u989c\u8272\u7684\u679a\u4e3e\uff1a<\/p>\n<p>namespace ObjectInitializers; enum PointColorEnum<br \/>\n{<br \/>\nLightBlue, BloodRed, Gold<br \/>\n}<\/p>\n<p>Now, update the Point class as follows:<br \/>\n\u73b0\u5728\uff0c\u66f4\u65b0 Point \u7c7b\uff0c\u5982\u4e0b\u6240\u793a\uff1a<\/p>\n<p>class Point<br \/>\n{<br \/>\npublic int X { get; set; } public int Y { get; set; }<br \/>\npublic PointColorEnum Color{ get; set; }<\/p>\n<p>public Point(int xVal, int yVal)<br \/>\n{<br \/>\nX = xVal;<br \/>\nY = yVal;<br \/>\nColor = PointColorEnum.Gold;<br \/>\n}<\/p>\n<p>public Point(PointColorEnum ptColor)<br \/>\n{<br \/>\nColor = ptColor;<br \/>\n}<\/p>\n<p>public Point() : this(PointColorEnum.BloodRed){ }<\/p>\n<p>public void DisplayStats()<br \/>\n{<br \/>\nConsole.WriteLine(&quot;[{0}, {1}]&quot;, X, Y); Console.WriteLine(&quot;Point is {0}&quot;, Color);<br \/>\n}<br \/>\n}<\/p>\n<p>With this new constructor, you can now create a gold point (positioned at 90, 20) as follows:<br \/>\n\u4f7f\u7528\u6b64\u65b0\u6784\u9020\u51fd\u6570\uff0c\u60a8\u73b0\u5728\u53ef\u4ee5\u521b\u5efa\u4e00\u4e2a\u9ec4\u91d1\u70b9\uff08\u4f4d\u4e8e 90\uff0c 20\uff09\uff0c\u5982\u4e0b\u6240\u793a\uff1a<\/p>\n<p>\/\/ Calling a more interesting custom constructor with init syntax. Point goldPoint = new Point(PointColorEnum.Gold){ X = 90, Y = 20 }; goldPoint.DisplayStats();<\/p>\n<h2>Initializing Data with Initialization Syntax<\/h2>\n<p>\u4f7f\u7528\u521d\u59cb\u5316\u8bed\u6cd5\u521d\u59cb\u5316\u6570\u636e<\/p>\n<p>As briefly mentioned earlier in this chapter (and fully examined in Chapter 6), the \u201chas-a\u201d relationship allows you to compose new classes by defining member variables of existing classes. For example, assume you now have a Rectangle class, which makes use of the Point type to represent its upper-left\/bottom-right coordinates. Since automatic properties set all fields of class variables to null, you will implement this new class using \u201ctraditional\u201d property syntax.<br \/>\n\u5982\u672c\u7ae0\u524d\u9762\u7b80\u8981\u63d0\u5230\u7684\uff08\u5e76\u5728\u7b2c6\u7ae0\u4e2d\u8fdb\u884c\u4e86\u5168\u9762\u7814\u7a76\uff09\uff0c\u201chas-a\u201d\u5173\u7cfb\u5141\u8bb8\u60a8\u901a\u8fc7\u5b9a\u4e49\u73b0\u6709\u7c7b\u7684\u6210\u5458\u53d8\u91cf\u6765\u7ec4\u5408\u65b0\u7c7b\u3002\u4f8b\u5982\uff0c\u5047\u8bbe\u60a8\u73b0\u5728\u6709\u4e00\u4e2a Rectangle \u7c7b\uff0c\u8be5\u7c7b\u4f7f\u7528 Point \u7c7b\u578b\u6765\u8868\u793a\u5176\u5de6\u4e0a\u89d2\/\u53f3\u4e0b\u89d2\u5750\u6807\u3002\u7531\u4e8e\u81ea\u52a8\u5c5e\u6027\u5c06\u7c7b\u53d8\u91cf\u7684\u6240\u6709\u5b57\u6bb5\u8bbe\u7f6e\u4e3a null\uff0c\u56e0\u6b64\u60a8\u5c06\u4f7f\u7528\u201c\u4f20\u7edf\u201d\u5c5e\u6027\u8bed\u6cd5\u5b9e\u73b0\u6b64\u65b0\u7c7b\u3002<\/p>\n<p>namespace ObjectInitializers; class Rectangle<br \/>\n{<br \/>\nprivate Point topLeft = new Point(); private Point bottomRight = new Point();<\/p>\n<p>public Point TopLeft<br \/>\n{<br \/>\nget { return topLeft; } set { topLeft = value; }<br \/>\n}<br \/>\npublic Point BottomRight<br \/>\n{<br \/>\nget { return bottomRight; } set { bottomRight = value; }<br \/>\n}<\/p>\n<p>public void DisplayStats()<br \/>\n{<br \/>\nConsole.WriteLine(&quot;[TopLeft: {0}, {1}, {2} BottomRight: {3}, {4}, {5}]&quot;, topLeft.X, topLeft.Y, topLeft.Color,<br \/>\nbottomRight.X, bottomRight.Y, bottomRight.Color);<br \/>\n}<br \/>\n}<\/p>\n<p>Using object initialization syntax, you could create a new Rectangle variable and set the inner Points as follows:<br \/>\n\u4f7f\u7528\u5bf9\u8c61\u521d\u59cb\u5316\u8bed\u6cd5\uff0c\u60a8\u53ef\u4ee5\u521b\u5efa\u4e00\u4e2a\u65b0\u7684 Rectangle \u53d8\u91cf\u5e76\u8bbe\u7f6e\u5185\u90e8\u70b9s\uff0c\u5982\u4e0b\u6240\u793a\uff1a<\/p>\n<p>\/\/ Create and initialize a Rectangle. Rectangle myRect = new Rectangle<br \/>\n{<br \/>\nTopLeft = new Point { X = 10, Y = 10 }, BottomRight = new Point { X = 200, Y = 200}<br \/>\n};<\/p>\n<p>Again, the benefit of object initialization syntax is that it basically decreases the number of keystrokes (assuming there is not a suitable constructor). Here is the traditional approach to establishing a similar Rectangle:<br \/>\n\u540c\u6837\uff0c\u5bf9\u8c61\u521d\u59cb\u5316\u8bed\u6cd5\u7684\u597d\u5904\u662f\u5b83\u57fa\u672c\u4e0a\u51cf\u5c11\u4e86\u51fb\u952e\u6b21\u6570\uff08\u5047\u8bbe\u6ca1\u6709\u5408\u9002\u7684\u6784\u9020\u51fd\u6570\uff09\u3002\u4ee5\u4e0b\u662f\u5efa\u7acb\u7c7b\u4f3c\u77e9\u5f62\u7684\u4f20\u7edf\u65b9\u6cd5\uff1a<\/p>\n<p>\/\/ Old-school approach. Rectangle r = new Rectangle(); Point p1 = new Point();<br \/>\np1.X = 10;<br \/>\np1.Y = 10;<\/p>\n<p>r.TopLeft = p1;<br \/>\nPoint p2 = new Point(); p2.X = 200;<br \/>\np2.Y = 200;<br \/>\nr.BottomRight = p2;<\/p>\n<p>While you might feel object initialization syntax can take a bit of getting used to, once you get comfortable with the code, you will be quite pleased at how quickly you can establish the state of a new object with minimal fuss and bother.<br \/>\n\u867d\u7136\u60a8\u53ef\u80fd\u89c9\u5f97\u5bf9\u8c61\u521d\u59cb\u5316\u8bed\u6cd5\u53ef\u80fd\u9700\u8981\u4e00\u4e9b\u65f6\u95f4\u6765\u9002\u5e94\uff0c\u4f46\u4e00\u65e6\u60a8\u719f\u6089\u4e86\u4ee3\u7801\uff0c\u60a8\u5c31\u4f1a\u5bf9\u4ee5\u6700\u5c11\u7684\u5927\u60ca\u5c0f\u602a\u548c\u9ebb\u70e6\u5feb\u901f\u5efa\u7acb\u65b0\u5bf9\u8c61\u7684\u72b6\u6001\u611f\u5230\u975e\u5e38\u6ee1\u610f\u3002<\/p>\n<h2>Working with Constant and Read-Only Field Data<\/h2>\n<p>\u4f7f\u7528\u5e38\u91cf\u548c\u53ea\u8bfb\u5b57\u6bb5\u6570\u636e<\/p>\n<p>Sometimes you need a property that you do not want changed at all, also known as immutable, either from the time it was compiled or after it was set during construction. We have already explored one example with init-only setters. Now we will examine constants and read-only fields.<br \/>\n\u6709\u65f6\uff0c\u60a8\u9700\u8981\u4e00\u4e2a\u6839\u672c\u4e0d\u60f3\u66f4\u6539\u7684\u5c5e\u6027\uff0c\u4e5f\u79f0\u4e3a\u4e0d\u53ef\u53d8\u5c5e\u6027\uff0c\u65e0\u8bba\u662f\u4ece\u7f16\u8bd1\u65f6\u8fd8\u662f\u5728\u6784\u9020\u671f\u95f4\u8bbe\u7f6e\u4e4b\u540e\u3002\u6211\u4eec\u5df2\u7ecf\u7528\u4ec5\u521d\u59cb\u5316\u7684 setter \u63a2\u7d22\u4e86\u4e00\u4e2a\u793a\u4f8b\u3002\u73b0\u5728\u6211\u4eec\u5c06\u68c0\u67e5\u5e38\u91cf\u548c\u53ea\u8bfb\u5b57\u6bb5\u3002<\/p>\n<h2>Understanding Constant Field Data<\/h2>\n<p>\u4e86\u89e3\u5e38\u91cf\u573a\u6570\u636e<\/p>\n<p>C# offers the const keyword to define constant data, which can never change after the initial assignment. As you might guess, this can be helpful when you are defining a set of known values for use in your applications that are logically connected to a given class or structure.<br \/>\nC# \u63d0\u4f9b\u4e86 const \u5173\u952e\u5b57\u6765\u5b9a\u4e49\u5e38\u91cf\u6570\u636e\uff0c\u8be5\u5e38\u91cf\u6570\u636e\u5728\u521d\u59cb\u8d4b\u503c\u540e\u6c38\u8fdc\u4e0d\u4f1a\u66f4\u6539\u3002\u6b63\u5982\u60a8\u53ef\u80fd\u731c\u5230\u7684\u90a3\u6837\uff0c\u5f53\u60a8\u5b9a\u4e49\u4e00\u7ec4\u5df2\u77e5\u503c\u4ee5\u5728\u903b\u8f91\u4e0a\u8fde\u63a5\u5230\u7ed9\u5b9a\u7c7b\u6216\u7ed3\u6784\u7684\u5e94\u7528\u7a0b\u5e8f\u4e2d\u4f7f\u7528\u65f6\uff0c\u8fd9\u4f1a\u5f88\u6709\u5e2e\u52a9\u3002<\/p>\n<p>Assume you are building a utility class named MyMathClass that needs to define a value for pi (which you will assume to be 3.14 for simplicity). Begin by creating a new Console Application project named ConstData and add a file named MyMathClass.cs. Given that you would not want to allow other developers to change this value in code, pi could be modeled with the following constant:<br \/>\n\u5047\u8bbe\u60a8\u6b63\u5728\u6784\u5efa\u4e00\u4e2a\u540d\u4e3a MyMathClass \u7684\u5b9e\u7528\u7a0b\u5e8f\u7c7b\uff0c\u8be5\u5b9e\u7528\u7a0b\u5e8f\u7c7b\u9700\u8981\u4e3a pi \u5b9a\u4e49\u4e00\u4e2a\u503c\uff08\u4e3a\u7b80\u5355\u8d77\u89c1\uff0c\u60a8\u5c06\u5047\u8bbe\u8be5\u503c\u4e3a 3.14\uff09\u3002\u9996\u5148\u521b\u5efa\u4e00\u4e2a\u540d\u4e3a ConstData \u7684\u65b0\u63a7\u5236\u53f0\u5e94\u7528\u7a0b\u5e8f\u9879\u76ee\uff0c\u5e76\u6dfb\u52a0\u4e00\u4e2a\u540d\u4e3a MyMathClass.cs \u7684\u6587\u4ef6\u3002\u9274\u4e8e\u60a8\u4e0d\u5e0c\u671b\u5141\u8bb8\u5176\u4ed6\u5f00\u53d1\u4eba\u5458\u5728\u4ee3\u7801\u4e2d\u66f4\u6539\u6b64\u503c\uff0c\u5219\u53ef\u4ee5\u4f7f\u7528\u4ee5\u4e0b\u5e38\u91cf\u5bf9 pi \u8fdb\u884c\u5efa\u6a21\uff1a<\/p>\n<p>\/\/MyMathClass.cs namespace ConstData; class MyMathClass<br \/>\n{<br \/>\npublic const double PI = 3.14;<br \/>\n}<\/p>\n<p>Update the code in the Program.cs file to match this:<br \/>\n\u66f4\u65b0\u7a0b\u5e8f.cs\u6587\u4ef6\u4e2d\u7684\u4ee3\u7801\u4ee5\u5339\u914d\u4ee5\u4e0b\u5185\u5bb9\uff1a<\/p>\n<p>using ConstData;<br \/>\nConsole.WriteLine(&quot;<strong><strong><em> Fun with Const <\/em><\/strong><\/strong>\\n&quot;); Console.WriteLine(&quot;The value of PI is: {0}&quot;, MyMathClass.PI);<br \/>\n\/\/ Error! Can't change a constant!<br \/>\n\/\/ MyMathClass.PI = 3.1444; Console.ReadLine();<\/p>\n<p>Notice that you are referencing the constant data defined by MyMathClass using a class name prefix (i.e.,<br \/>\nMyMathClass.PI). This is because constant fields of a class are implicitly static. However, it is permissible to define and access a local constant variable within the scope of a method or property. Here is an example:<br \/>\n\u8bf7\u6ce8\u610f\uff0c\u60a8\u6b63\u5728\u4f7f\u7528\u7c7b\u540d\u524d\u7f00\uff08\u5373MyMathClass.PI\uff09\u3002 \u8fd9\u662f\u56e0\u4e3a\u7c7b\u7684\u5e38\u91cf\u5b57\u6bb5\u662f\u9690\u5f0f\u9759\u6001\u7684\u3002\u4f46\u662f\uff0c\u5141\u8bb8\u5728\u65b9\u6cd5\u6216\u5c5e\u6027\u8303\u56f4\u5185\u5b9a\u4e49\u548c\u8bbf\u95ee\u5c40\u90e8\u5e38\u91cf\u53d8\u91cf\u3002\u4e0b\u9762\u662f\u4e00\u4e2a\u793a\u4f8b\uff1a<\/p>\n<p>static void LocalConstStringVariable()<br \/>\n{<br \/>\n\/\/ A local constant data point can be directly accessed.<\/p>\n<p>const string fixedStr = &quot;Fixed string Data&quot;; Console.WriteLine(fixedStr);<\/p>\n<p>\/\/ Error!<br \/>\n\/\/ fixedStr = &quot;This will not work!&quot;;<br \/>\n}<\/p>\n<p>Regardless of where you define a constant piece of data, the one point to always remember is that the initial value assigned to the constant must be specified at the time you define the constant. Assigning the value of pi in a class constructor, as shown in the following code, produces a compilation error:<br \/>\n\u65e0\u8bba\u5728\u4f55\u5904\u5b9a\u4e49\u5e38\u91cf\u6570\u636e\uff0c\u59cb\u7ec8\u8981\u8bb0\u4f4f\u7684\u4e00\u70b9\u662f\uff0c\u5fc5\u987b\u5728\u5b9a\u4e49\u5e38\u91cf\u65f6\u6307\u5b9a\u5206\u914d\u7ed9\u5e38\u91cf\u7684\u521d\u59cb\u503c\u3002\u5728\u7c7b\u6784\u9020\u51fd\u6570\u4e2d\u5206\u914d pi \u7684\u503c\uff08\u5982\u4ee5\u4e0b\u4ee3\u7801\u6240\u793a\uff09\u4f1a\u4ea7\u751f\u7f16\u8bd1\u9519\u8bef\uff1a<\/p>\n<p>class MyMathClass<br \/>\n{<br \/>\n\/\/ Try to set PI in constructor? public const double PI;<\/p>\n<p>public MyMathClass()<br \/>\n{<br \/>\n\/\/ Not possible- must assign at time of declaration. PI = 3.14;<br \/>\n}<br \/>\n}<\/p>\n<p>The reason for this restriction has to do with the fact that the value of constant data must be known at compile time. Constructors (or any other method), as you know, are invoked at runtime.<br \/>\n\u6b64\u9650\u5236\u7684\u539f\u56e0\u4e0e\u5e38\u91cf\u6570\u636e\u7684\u503c\u5fc5\u987b\u5728\u7f16\u8bd1\u65f6\u5df2\u77e5\u8fd9\u4e00\u4e8b\u5b9e\u6709\u5173\u3002\u5982\u60a8\u6240\u77e5\uff0c\u6784\u9020\u51fd\u6570\uff08\u6216\u4efb\u4f55\u5176\u4ed6\u65b9\u6cd5\uff09\u662f\u5728\u8fd0\u884c\u65f6\u8c03\u7528\u7684\u3002<\/p>\n<h2>Constant Interpolated Strings (New 10.0)<\/h2>\n<p>\u5e38\u91cf\u5185\u63d2\u5b57\u7b26\u4e32\uff08\u65b0 10.0\uff09<\/p>\n<p>Introduced in C# 10, const string values can use string interpolation in their assignment statements as long as all of the components that are used are also const strings. As a trivial example, add the following code to the top-level statements:<br \/>\n\u5728 C# 10 \u4e2d\u5f15\u5165\u7684 const \u5b57\u7b26\u4e32\u503c\u53ef\u4ee5\u5728\u5176\u8d4b\u503c\u8bed\u53e5\u4e2d\u4f7f\u7528\u5b57\u7b26\u4e32\u5185\u63d2\uff0c\u53ea\u8981\u4f7f\u7528\u7684\u6240\u6709\u7ec4\u4ef6\u4e5f\u662f const \u5b57\u7b26\u4e32\u3002\u4f5c\u4e3a\u4e00\u4e2a\u7b80\u5355\u7684\u793a\u4f8b\uff0c\u5c06\u4ee5\u4e0b\u4ee3\u7801\u6dfb\u52a0\u5230\u9876\u7ea7\u8bed\u53e5\u4e2d\uff1a<\/p>\n<p>Console.WriteLine(&quot;=&gt; Constant String Interpolation:&quot;); const string foo = &quot;Foo&quot;;<br \/>\nconst string bar = &quot;Bar&quot;;<br \/>\nconst string foobar = $&quot;{foo}{bar}&quot;; Console.WriteLine(foobar);<\/p>\n<h2>Understanding Read-Only Fields<\/h2>\n<p>\u4e86\u89e3\u53ea\u8bfb\u5b57\u6bb5<br \/>\nClosely related to constant data is the notion of read-only field data (which should not be confused with a read-only property). Like a constant, a read-only field cannot be changed after the initial assignment or you will receive a compile-time error. However, unlike a constant, the value assigned to a read-only field can be determined at runtime and, therefore, can legally be assigned within the scope of a constructor but nowhere else.<br \/>\n\u4e0e\u5e38\u91cf\u6570\u636e\u5bc6\u5207\u76f8\u5173\u7684\u662f\u53ea\u8bfb\u5b57\u6bb5\u6570\u636e\u7684\u6982\u5ff5\uff08\u4e0d\u5e94\u5c06\u5176\u4e0e\u53ea\u8bfb\u5c5e\u6027\u6df7\u6dc6\uff09\u3002\u4e0e\u5e38\u91cf\u4e00\u6837\uff0c\u53ea\u8bfb\u5b57\u6bb5\u5728\u521d\u59cb\u8d4b\u503c\u540e\u65e0\u6cd5\u66f4\u6539\uff0c\u5426\u5219\u60a8\u5c06\u6536\u5230\u7f16\u8bd1\u65f6\u9519\u8bef\u3002\u4f46\u662f\uff0c\u4e0e\u5e38\u91cf\u4e0d\u540c\uff0c\u5206\u914d\u7ed9\u53ea\u8bfb\u5b57\u6bb5\u7684\u503c\u53ef\u4ee5\u5728\u8fd0\u884c\u65f6\u786e\u5b9a\uff0c\u56e0\u6b64\u53ef\u4ee5\u5408\u6cd5\u5730\u5728\u6784\u9020\u51fd\u6570\u7684\u8303\u56f4\u5185\u5206\u914d\uff0c\u4f46\u5728\u5176\u4ed6\u5730\u65b9\u4e0d\u80fd\u3002<br \/>\nThis can be helpful when you do not know the value of a field until runtime, perhaps because you need to read an external file to obtain the value but want to ensure that the value will not change after that point. For the sake of illustration, assume the following update to MyMathClass:<br \/>\n\u5f53\u60a8\u5728\u8fd0\u884c\u65f6\u4e4b\u524d\u4e0d\u77e5\u9053\u5b57\u6bb5\u7684\u503c\u65f6\uff0c\u8fd9\u53ef\u80fd\u4f1a\u5f88\u6709\u5e2e\u52a9\uff0c\u8fd9\u53ef\u80fd\u662f\u56e0\u4e3a\u60a8\u9700\u8981\u8bfb\u53d6\u5916\u90e8\u6587\u4ef6\u4ee5\u83b7\u53d6\u8be5\u503c\uff0c\u4f46\u5e0c\u671b\u786e\u4fdd\u8be5\u503c\u5728\u8be5\u70b9\u4e4b\u540e\u4e0d\u4f1a\u66f4\u6539\u3002\u4e3a\u4e86\u4fbf\u4e8e\u8bf4\u660e\uff0c\u5047\u8bbeMyMathClass\u8fdb\u884c\u4e86\u4ee5\u4e0b\u66f4\u65b0\uff1a<\/p>\n<p>class MyMathClass<br \/>\n{<br \/>\n\/\/ Read-only fields can be assigned in constructors,<br \/>\n\/\/ but nowhere else. public readonly double PI;<\/p>\n<p>public MyMathClass ()<br \/>\n{<br \/>\nPI = 3.14;<br \/>\n}<br \/>\n}<\/p>\n<p>Again, any attempt to make assignments to a field marked readonly outside the scope of a constructor results in a compiler error.<br \/>\n\u540c\u6837\uff0c\u4efb\u4f55\u5c1d\u8bd5\u5728\u6784\u9020\u51fd\u6570\u8303\u56f4\u4e4b\u5916\u5bf9\u6807\u8bb0\u4e3a\u53ea\u8bfb\u7684\u5b57\u6bb5\u8fdb\u884c\u8d4b\u503c\u90fd\u4f1a\u5bfc\u81f4\u7f16\u8bd1\u5668\u9519\u8bef\u3002<\/p>\n<p>class MyMathClass<br \/>\n{<br \/>\npublic readonly double PI; public MyMathClass ()<br \/>\n{<br \/>\nPI = 3.14;<br \/>\n}<\/p>\n<p>\/\/ Error!<br \/>\npublic void ChangePI()<br \/>\n{ PI = 3.14444;}<br \/>\n}<\/p>\n<h2>Understanding Static Read-Only Fields<\/h2>\n<p>\u4e86\u89e3\u9759\u6001\u53ea\u8bfb\u5b57\u6bb5<\/p>\n<p>Unlike a constant field, read-only fields are not implicitly static. Thus, if you want to expose PI from the class level, you must explicitly use the static keyword. If you know the value of a static read-only field at compile time, the initial assignment looks similar to that of a constant (however, in this case, it would be easier to simply use the const keyword in the first place, as you are assigning the data field at the time of declaration).<br \/>\n\u4e0e\u5e38\u91cf\u5b57\u6bb5\u4e0d\u540c\uff0c\u53ea\u8bfb\u5b57\u6bb5\u4e0d\u662f\u9690\u5f0f\u9759\u6001\u7684\u3002\u56e0\u6b64\uff0c\u5982\u679c\u8981\u4ece\u7c7b\u7ea7\u522b\u516c\u5f00 PI\uff0c\u5219\u5fc5\u987b\u663e\u5f0f\u4f7f\u7528 static \u5173\u952e\u5b57\u3002\u5982\u679c\u60a8\u5728\u7f16\u8bd1\u65f6\u77e5\u9053\u9759\u6001\u53ea\u8bfb\u5b57\u6bb5\u7684\u503c\uff0c\u5219\u521d\u59cb\u8d4b\u503c\u770b\u8d77\u6765\u7c7b\u4f3c\u4e8e\u5e38\u91cf\u7684\u503c\uff08\u4f46\u662f\uff0c\u5728\u8fd9\u79cd\u60c5\u51b5\u4e0b\uff0c\u9996\u5148\u7b80\u5355\u5730\u4f7f\u7528 const \u5173\u952e\u5b57\u4f1a\u66f4\u5bb9\u6613\uff0c\u56e0\u4e3a\u60a8\u5728\u58f0\u660e\u65f6\u5206\u914d\u6570\u636e\u5b57\u6bb5\uff09\u3002<\/p>\n<p>class MyMathClass<br \/>\n{<br \/>\npublic static readonly double PI = 3.14;<br \/>\n}<\/p>\n<p>\/\/Program.cs using ConstData;<\/p>\n<p>Console.WriteLine(&quot;<strong><strong><em> Fun with Const <\/em><\/strong><\/strong>&quot;); Console.WriteLine(&quot;The value of PI is: {0}&quot;, MyMathClass.PI); Console.ReadLine();<\/p>\n<p>However, if the value of a static read-only field is not known until runtime, you must use a static constructor as described earlier in this chapter.<br \/>\n\u4f46\u662f\uff0c\u5982\u679c\u9759\u6001\u53ea\u8bfb\u5b57\u6bb5\u7684\u503c\u5728\u8fd0\u884c\u65f6\u4e4b\u524d\u662f\u672a\u77e5\u7684\uff0c\u5219\u5fc5\u987b\u4f7f\u7528\u672c\u7ae0\u524d\u9762\u6240\u8ff0\u7684\u9759\u6001\u6784\u9020\u51fd\u6570\u3002<\/p>\n<p>class MyMathClass<br \/>\n{<br \/>\npublic static readonly double PI;<\/p>\n<p>static MyMathClass()<br \/>\n{ PI = 3.14; }<br \/>\n}<\/p>\n<h2>Understanding Partial Classes<\/h2>\n<p>\u4e86\u89e3\u5206\u90e8\u7c7b<\/p>\n<p>When working with classes, it is important to understand the role of the C# partial keyword. The partial keyword allows for a single class to be partitioned across multiple code files. When you scaffold Entity Framework Core classes from a database, the created classes are all created as partial classes. This way, any code that you have written to augment those files is not overwritten, presuming your code is in separate class files marked with the partial keyword. Another reason is that maybe your class has grown over time into something difficult to manage, and as an intermediate step toward refactoring that class, you can split it up into partials.<br \/>\n\u4f7f\u7528\u7c7b\u65f6\uff0c\u4e86\u89e3 C# \u5206\u90e8\u5173\u952e\u5b57\u7684\u4f5c\u7528\u975e\u5e38\u91cd\u8981\u3002partial \u5173\u952e\u5b57\u5141\u8bb8\u8de8\u591a\u4e2a\u4ee3\u7801\u6587\u4ef6\u5bf9\u5355\u4e2a\u7c7b\u8fdb\u884c\u5206\u533a\u3002\u4ece\u6570\u636e\u5e93\u642d\u5efa\u5b9e\u4f53\u6846\u67b6\u6838\u5fc3\u7c7b\u7684\u57fa\u67b6\u65f6\uff0c\u521b\u5efa\u7684\u7c7b\u90fd\u5c06\u521b\u5efa\u4e3a\u5206\u90e8\u7c7b\u3002\u8fd9\u6837\uff0c\u60a8\u4e3a\u6269\u5145\u8fd9\u4e9b\u6587\u4ef6\u800c\u7f16\u5199\u7684\u4efb\u4f55\u4ee3\u7801\u90fd\u4e0d\u4f1a\u88ab\u8986\u76d6\uff0c\u524d\u63d0\u662f\u60a8\u7684\u4ee3\u7801\u4f4d\u4e8e\u6807\u6709 partial \u5173\u952e\u5b57\u7684\u5355\u72ec\u7c7b\u6587\u4ef6\u4e2d\u3002\u53e6\u4e00\u4e2a\u539f\u56e0\u662f\uff0c\u968f\u7740\u65f6\u95f4\u7684\u63a8\u79fb\uff0c\u60a8\u7684\u7c7b\u53ef\u80fd\u5df2\u7ecf\u53d1\u5c55\u6210\u4e3a\u96be\u4ee5\u7ba1\u7406\u7684\u4e1c\u897f\uff0c\u4f5c\u4e3a\u91cd\u6784\u8be5\u7c7b\u7684\u4e2d\u95f4\u6b65\u9aa4\uff0c\u60a8\u53ef\u4ee5\u5c06\u5176\u62c6\u5206\u4e3a\u90e8\u5206\u3002<\/p>\n<p>In C#, you can partition a single class across multiple code files to isolate the boilerplate code from more readily useful (and complex) members. To illustrate where partial classes could be useful, open the EmployeeApp project you created previously in this chapter in Visual Studio or Visual Studio Code and then open the Employee.cs file for editing. As you recall, this single file contains code of all aspects of the class.<br \/>\n\u5728 C# \u4e2d\uff0c\u53ef\u4ee5\u8de8\u591a\u4e2a\u4ee3\u7801\u6587\u4ef6\u5bf9\u5355\u4e2a\u7c7b\u8fdb\u884c\u5206\u533a\uff0c\u4ee5\u5c06\u6837\u677f\u4ee3\u7801\u4e0e\u66f4\u6709\u7528\uff08\u548c\u590d\u6742\uff09\u7684\u6210\u5458\u9694\u79bb\u5f00\u6765\u3002\u82e5\u8981\u8bf4\u660e\u5206\u90e8\u7c7b\u5728\u54ea\u4e9b\u65b9\u9762\u53ef\u80fd\u6709\u7528\uff0c\u8bf7\u6253\u5f00\u60a8\u4e4b\u524d\u5728\u672c\u7ae0\u4e2d\u521b\u5efa\u7684 Visual Studio \u6216 Visual Studio Code \u4e2d\u7684 EmployeeApp \u9879\u76ee\uff0c\u7136\u540e\u6253\u5f00 Employee.cs \u6587\u4ef6\u8fdb\u884c\u7f16\u8f91\u3002\u60a8\u8fd8\u8bb0\u5f97\uff0c\u8fd9\u4e2a\u6587\u4ef6\u5305\u542b\u7c7b\u7684\u6240\u6709\u65b9\u9762\u7684\u4ee3\u7801\u3002<\/p>\n<p>class Employee<br \/>\n{<br \/>\n\/\/ Field Data<\/p>\n<p>\/\/ Constructors<\/p>\n<p>\/\/ Methods<\/p>\n<p>\/\/ Properties<br \/>\n}<\/p>\n<p>Using partial classes, you could choose to move (for example) the properties, constructors, and field data into a new file named Employee.Core.cs (the name of the file is irrelevant). The first step is to add the partial keyword to the current class definition and cut the code to be placed into the new file.<br \/>\n\u4f7f\u7528\u5206\u90e8\u7c7b\uff0c\u53ef\u4ee5\u9009\u62e9\u5c06\u5c5e\u6027\u3001\u6784\u9020\u51fd\u6570\u548c\u5b57\u6bb5\u6570\u636e\uff08\u4f8b\u5982\uff09\u79fb\u52a8\u5230\u540d\u4e3a Employee.Core.cs \u7684\u65b0\u6587\u4ef6\u4e2d\uff08\u6587\u4ef6\u540d\u65e0\u5173\u7d27\u8981\uff09\u3002\u7b2c\u4e00\u6b65\u662f\u5c06 partial \u5173\u952e\u5b57\u6dfb\u52a0\u5230\u5f53\u524d\u7c7b\u5b9a\u4e49\u4e2d\uff0c\u5e76\u526a\u5207\u8981\u653e\u5165\u65b0\u6587\u4ef6\u4e2d\u7684\u4ee3\u7801\u3002<\/p>\n<p>\/\/ Employee.cs<br \/>\npartial class Employee<br \/>\n{<br \/>\n\/\/ Methods<\/p>\n<p>\/\/ Properties<br \/>\n}<\/p>\n<p>Next, assuming you have inserted a new class file into your project, you can move the data fields and properties to the new file using a simple cut-and-paste operation. In addition, you must add the partial keyword to this aspect of the class definition. Here is an example:<br \/>\n\u63a5\u4e0b\u6765\uff0c\u5047\u8bbe\u60a8\u5df2\u5c06\u65b0\u7684\u7c7b\u6587\u4ef6\u63d2\u5165\u5230\u9879\u76ee\u4e2d\uff0c\u5219\u53ef\u4ee5\u4f7f\u7528\u7b80\u5355\u7684\u526a\u5207\u548c\u7c98\u8d34\u64cd\u4f5c\u5c06\u6570\u636e\u5b57\u6bb5\u548c\u5c5e\u6027\u79fb\u52a8\u5230\u65b0\u6587\u4ef6\u4e2d\u3002\u6b64\u5916\uff0c\u8fd8\u5fc5\u987b\u5c06 partial \u5173\u952e\u5b57\u6dfb\u52a0\u5230\u7c7b\u5b9a\u4e49\u7684\u8fd9\u4e00\u65b9\u9762\u3002\u4e0b\u9762\u662f\u4e00\u4e2a\u793a\u4f8b\uff1a<\/p>\n<p>\/\/ Employee.Core.cs<br \/>\npartial class Employee<br \/>\n{<br \/>\n\/\/ Field data<\/p>\n<p>\/\/ Properties<br \/>\n}<\/p>\n<p>\u25a0 Note  remember that each of the partial classes must be marked with the partial keyword!<br \/>\n\u8bf7\u6ce8\u610f\uff0c\u6bcf\u4e2a\u5206\u90e8\u7c7b\u90fd\u5fc5\u987b\u6807\u6709\u5206\u90e8\u5173\u952e\u5b57\uff01<\/p>\n<p>After you compile the modified project, you should see no difference whatsoever. The whole idea of a partial class is realized only during design time. After the application has been compiled, there is just a single, unified class within the assembly. The only requirement when defining partial types is that the type\u2019s name (Employee in this case) is identical and defined within the same .NET Core namespace.<br \/>\n\u7f16\u8bd1\u4fee\u6539\u540e\u7684\u9879\u76ee\u540e\uff0c\u60a8\u5e94\u8be5\u770b\u4e0d\u5230\u4efb\u4f55\u533a\u522b\u3002\u5206\u90e8\u7c7b\u7684\u6574\u4e2a\u601d\u60f3\u4ec5\u5728\u8bbe\u8ba1\u65f6\u5b9e\u73b0\u3002\u5e94\u7528\u7a0b\u5e8f\u7f16\u8bd1\u5b8c\u6210\u540e\uff0c\u53ea\u6709\u4e00\u4e2a\u7a0b\u5e8f\u96c6\u4e2d\u7684\u5355\u4e2a\u7edf\u4e00\u7c7b\u3002\u5b9a\u4e49\u5206\u90e8\u7c7b\u578b\u65f6\u7684\u552f\u4e00\u8981\u6c42\u662f\u7c7b\u578b\u7684\u540d\u79f0\uff08\u5728\u672c\u4f8b\u4e2d\u4e3a Employee\uff09\u76f8\u540c\uff0c\u5e76\u4e14\u5728\u540c\u4e00 .NET Core \u547d\u540d\u7a7a\u95f4\u4e2d\u5b9a\u4e49\u3002<br \/>\nRecall from the discussion of top-level statements, any methods in top-level statements must be a local function. The top-level statements are implicitly defined in a partial Program class, allowing for the creation of another partial Program class to hold regular methods.<br \/>\n\u56de\u60f3\u4e00\u4e0b\u9876\u7ea7\u8bed\u53e5\u7684\u8ba8\u8bba\uff0c\u9876\u7ea7\u8bed\u53e5\u4e2d\u7684\u4efb\u4f55\u65b9\u6cd5\u90fd\u5fc5\u987b\u662f\u5c40\u90e8\u51fd\u6570\u3002\u9876\u7ea7\u8bed\u53e5\u5728\u5206\u90e8 Program \u7c7b\u4e2d\u9690\u5f0f\u5b9a\u4e49\uff0c\u5141\u8bb8\u521b\u5efa\u53e6\u4e00\u4e2a\u5206\u90e8 Program \u7c7b\u6765\u4fdd\u5b58\u5e38\u89c4\u65b9\u6cd5\u3002<\/p>\n<p>Create a new console application named FunWithPartials and add a new class file named Program.<br \/>\n\u521b\u5efa\u4e00\u4e2a\u540d\u4e3a FunWithPartss \u7684\u65b0\u63a7\u5236\u53f0\u5e94\u7528\u7a0b\u5e8f\uff0c\u5e76\u6dfb\u52a0\u4e00\u4e2a\u540d\u4e3a Program \u7684\u65b0\u7c7b\u6587\u4ef6\u3002<\/p>\n<p>Partial.cs. Update the code to the following:<\/p>\n<p>public partial class Program<br \/>\n{<br \/>\npublic static string SayHello() =&gt; return &quot;Hello&quot;;<br \/>\n}<\/p>\n<p>Now you can call that method from your top-level statements in the Program.cs file, like this:<br \/>\n\u73b0\u5728\uff0c\u60a8\u53ef\u4ee5\u4ece Program.cs \u6587\u4ef6\u4e2d\u7684\u9876\u7ea7\u8bed\u53e5\u8c03\u7528\u8be5\u65b9\u6cd5\uff0c\u5982\u4e0b\u6240\u793a\uff1a<\/p>\n<p>Console.WriteLine(SayHello());<\/p>\n<p>Which method you use is a matter of preference.<br \/>\n\u60a8\u4f7f\u7528\u54ea\u79cd\u65b9\u6cd5\u662f\u4e00\u4e2a\u504f\u597d\u95ee\u9898\u3002<\/p>\n<h2>Records (New 9.0)<\/h2>\n<p>\u8bb0\u5f55\uff08\u65b0 9.0\uff09<\/p>\n<p>New in C# 9.0, record types are a special reference type that provide synthesized methods for equality using value semantics and data encapsulation. Record types can be created with immutable or standard properties. To start experimenting with records, create a new console application named FunWithRecords. Consider the following Car class, modified from the examples earlier in the chapter:<br \/>\n\u8bb0\u5f55\u7c7b\u578b\u662f C# 9.0 \u4e2d\u7684\u65b0\u589e\u529f\u80fd\uff0c\u662f\u4e00\u79cd\u7279\u6b8a\u7684\u5f15\u7528\u7c7b\u578b\uff0c\u5b83\u4f7f\u7528\u503c\u8bed\u4e49\u548c\u6570\u636e\u5c01\u88c5\u63d0\u4f9b\u76f8\u7b49\u7684\u7efc\u5408\u65b9\u6cd5\u3002\u53ef\u4ee5\u4f7f\u7528\u4e0d\u53ef\u53d8\u6216\u6807\u51c6\u5c5e\u6027\u521b\u5efa\u8bb0\u5f55\u7c7b\u578b\u3002\u82e5\u8981\u5f00\u59cb\u8bd5\u9a8c\u8bb0\u5f55\uff0c\u8bf7\u521b\u5efa\u4e00\u4e2a\u540d\u4e3a FunWithRecords \u7684\u65b0\u63a7\u5236\u53f0\u5e94\u7528\u7a0b\u5e8f\u3002\u8bf7\u8003\u8651\u4ee5\u4e0b Car \u7c7b\uff0c\u8be5\u7c7b\u662f\u4ece\u672c\u7ae0\u524d\u9762\u7684\u793a\u4f8b\u4fee\u6539\u800c\u6765\u7684\uff1a<\/p>\n<p>class Car<br \/>\n{<br \/>\npublic string Make { get; set; } public string Model { get; set; } public string Color { get; set; }<\/p>\n<p>public Car() {}<\/p>\n<p>public Car(string make, string model, string color)<br \/>\n{<br \/>\nMake = make;<\/p>\n<p>Model = model;<br \/>\nColor = color;<br \/>\n}<br \/>\n}<\/p>\n<p>As you well know by now, once you create an instance of this class, you can change any of the properties at run time. If the properties for the previous Car class need to be immutable, you can change its property definitions to use init-only setters, like this:<br \/>\n\u4f17\u6240\u5468\u77e5\uff0c\u4e00\u65e6\u521b\u5efa\u4e86\u6b64\u7c7b\u7684\u5b9e\u4f8b\uff0c\u5c31\u53ef\u4ee5\u5728\u8fd0\u884c\u65f6\u66f4\u6539\u4efb\u4f55\u5c5e\u6027\u3002\u5982\u679c\u4ee5\u524d\u7684 Car \u7c7b\u7684\u5c5e\u6027\u9700\u8981\u4e0d\u53ef\u53d8\uff0c\u5219\u53ef\u4ee5\u5c06\u5176\u5c5e\u6027\u5b9a\u4e49\u66f4\u6539\u4e3a\u4f7f\u7528\u4ec5\u521d\u59cb\u5316\u8d44\u6e90\u5e93\uff0c\u5982\u4e0b\u6240\u793a\uff1a<\/p>\n<p>public string Make { get; init; } public string Model { get; init; } public string Color { get; init; }<\/p>\n<p>To exercise this new class, the following code creates two instances of the Car class, one through object initialization and the other through the custom constructor. Update the Program.cs file to the following:<br \/>\n\u4e3a\u4e86\u7ec3\u4e60\u8fd9\u4e2a\u65b0\u7c7b\uff0c\u4e0b\u9762\u7684\u4ee3\u7801\u521b\u5efa Car \u7c7b\u7684\u4e24\u4e2a\u5b9e\u4f8b\uff0c\u4e00\u4e2a\u901a\u8fc7\u5bf9\u8c61\u521d\u59cb\u5316\uff0c\u53e6\u4e00\u4e2a\u901a\u8fc7\u81ea\u5b9a\u4e49\u6784\u9020\u51fd\u6570\u3002\u5c06\u7a0b\u5e8f.cs\u6587\u4ef6\u66f4\u65b0\u4e3a\u4ee5\u4e0b\u5185\u5bb9\uff1a<\/p>\n<p>using FunWithRecords; Console.WriteLine(&quot;Fun with Records!&quot;);<br \/>\n\/\/Use object initialization Car myCar = new Car<br \/>\n{<br \/>\nMake = &quot;Honda&quot;, Model = &quot;Pilot&quot;, Color = &quot;Blue&quot;<br \/>\n};<br \/>\nConsole.WriteLine(&quot;My car: &quot;); DisplayCarStats(myCar); Console.WriteLine();<br \/>\n\/\/Use the custom constructor<br \/>\nCar anotherMyCar = new Car(&quot;Honda&quot;, &quot;Pilot&quot;, &quot;Blue&quot;); Console.WriteLine(&quot;Another variable for my car: &quot;); DisplayCarStats(anotherMyCar);<br \/>\nConsole.WriteLine();<\/p>\n<p>\/\/Compile error if property is changed<br \/>\n\/\/myCar.Color = &quot;Red&quot;; Console.ReadLine();<br \/>\nstatic void DisplayCarStats(Car c)<br \/>\n{<br \/>\nConsole.WriteLine(&quot;Car Make: {0}&quot;, c.Make); Console.WriteLine(&quot;Car Model: {0}&quot;, c.Model); Console.WriteLine(&quot;Car Color: {0}&quot;, c.Color);<br \/>\n}<\/p>\n<p>As expected, both methods of object creation work, properties get displayed, and trying to change a property after construction raises a compilation error.<br \/>\n\u6b63\u5982\u9884\u671f\u7684\u90a3\u6837\uff0c\u4e24\u79cd\u5bf9\u8c61\u521b\u5efa\u65b9\u6cd5\u90fd\u6709\u6548\uff0c\u5c5e\u6027\u90fd\u4f1a\u663e\u793a\uff0c\u5e76\u4e14\u5728\u6784\u9020\u540e\u5c1d\u8bd5\u66f4\u6539\u5c5e\u6027\u4f1a\u5f15\u53d1\u7f16\u8bd1\u9519\u8bef\u3002<\/p>\n<h2>Immutable Record Types with Standard Property Syntax<\/h2>\n<p>\u5177\u6709\u6807\u51c6\u5c5e\u6027\u8bed\u6cd5\u7684\u4e0d\u53ef\u53d8\u8bb0\u5f55\u7c7b\u578b<\/p>\n<p>Creating an immutable Car record type using standard property syntax is similar to creating classes with immutable properties. To see this in action, add a new file named (CarRecord.cs) to your project and add the following code:<br \/>\n\u4f7f\u7528\u6807\u51c6\u5c5e\u6027\u8bed\u6cd5\u521b\u5efa\u4e0d\u53ef\u53d8\u7684 Car \u8bb0\u5f55\u7c7b\u578b\u7c7b\u4f3c\u4e8e\u521b\u5efa\u5177\u6709\u4e0d\u53ef\u53d8\u5c5e\u6027\u7684\u7c7b\u3002\u82e5\u8981\u67e5\u770b\u6b64\u64cd\u4f5c\u7684\u5b9e\u9645\u6548\u679c\uff0c\u8bf7\u5c06\u540d\u4e3a \uff08CarRecord.cs\uff09 \u7684\u65b0\u6587\u4ef6\u6dfb\u52a0\u5230\u9879\u76ee\u4e2d\uff0c\u5e76\u6dfb\u52a0\u4ee5\u4e0b\u4ee3\u7801\uff1a<\/p>\n<p>record CarRecord<br \/>\n{<br \/>\npublic string Make { get; init; } public string Model { get; init; } public string Color { get; init; }<\/p>\n<p>public CarRecord () {}<br \/>\npublic CarRecord (string make, string model, string color)<br \/>\n{<br \/>\nMake = make;<br \/>\nModel = model;<br \/>\nColor = color;<br \/>\n}<br \/>\n}<\/p>\n<p>\u25a0 Note  record types allow using the class keyword to help distinguish them from record structs, but the keyword is optional. Therefore record class and record mean the same thing.<br \/>\n\u6ce8\u610f\u8bb0\u5f55\u7c7b\u578b\u5141\u8bb8\u4f7f\u7528 class \u5173\u952e\u5b57\u6765\u5e2e\u52a9\u5c06\u5b83\u4eec\u4e0e\u8bb0\u5f55\u7ed3\u6784s \u533a\u5206\u5f00\u6765\uff0c\u4f46\u5173\u952e\u5b57\u662f\u53ef\u9009\u7684\u3002\u56e0\u6b64\uff0c\u8bb0\u5f55\u7c7b\u548c\u8bb0\u5f55\u7684\u542b\u4e49\u76f8\u540c\u3002<\/p>\n<p>You can confirm that the behavior is the same as the Car class with init-only settings by running the following code in Program.cs:<br \/>\n\u60a8\u53ef\u4ee5\u901a\u8fc7\u5728 Program.cs \u4e2d\u8fd0\u884c\u4ee5\u4e0b\u4ee3\u7801\u6765\u786e\u8ba4\u8be5\u884c\u4e3a\u4e0e\u5177\u6709\u4ec5\u521d\u59cb\u5316\u8bbe\u7f6e\u7684 Car \u7c7b\u76f8\u540c\uff1a<\/p>\n<p>using FunWithRecords;<\/p>\n<p>Console.WriteLine(&quot;<strong><strong><strong><strong><strong><strong><strong><em> RECORDS <\/em><strong>**<\/strong><\/strong><\/strong><\/strong><\/strong><\/strong><\/strong><\/strong>&quot;);<br \/>\n\/\/Use object initialization<br \/>\nCarRecord myCarRecord = new CarRecord<br \/>\n{<br \/>\nMake = &quot;Honda&quot;, Model = &quot;Pilot&quot;, Color = &quot;Blue&quot;<br \/>\n};<br \/>\nConsole.WriteLine(&quot;My car: &quot;); DisplayCarRecordStats(myCarRecord); Console.WriteLine();<\/p>\n<p>\/\/Use the custom constructor<br \/>\nCarRecord anotherMyCarRecord = new CarRecord(&quot;Honda&quot;, &quot;Pilot&quot;, &quot;Blue&quot;); Console.WriteLine(&quot;Another variable for my car: &quot;); Console.WriteLine(anotherMyCarRecord.ToString());<br \/>\nConsole.WriteLine();<\/p>\n<p>\/\/Compile error if property is changed<br \/>\n\/\/myCarRecord.Color = &quot;Red&quot;;<\/p>\n<p>Console.ReadLine();<\/p>\n<p>static void DisplayCarStats(Car c)<br \/>\n{<br \/>\nConsole.WriteLine(&quot;Car Make: {0}&quot;, c.Make); Console.WriteLine(&quot;Car Model: {0}&quot;, c.Model); Console.WriteLine(&quot;Car Color: {0}&quot;, c.Color);<br \/>\n}<\/p>\n<p>While we have not covered equality (next section) or inheritance (next chapter) with records, this first look at records does not seem like much of a benefit. The current Car example includes all of the plumbing code that we have come to expect. With one notable difference on the output: the ToString() method is fancied up for record types, as shown in this following sample output:<br \/>\n\u867d\u7136\u6211\u4eec\u8fd8\u6ca1\u6709\u4ecb\u7ecd\u8bb0\u5f55\u7684\u5e73\u7b49\uff08\u4e0b\u4e00\u8282\uff09\u6216\u7ee7\u627f\uff08\u4e0b\u4e00\u7ae0\uff09\uff0c\u4f46\u7b2c\u4e00\u6b21\u770b\u8bb0\u5f55\u4f3c\u4e4e\u5e76\u6ca1\u6709\u591a\u5927\u597d\u5904\u3002\u5f53\u524d\u7684 Car \u793a\u4f8b\u5305\u62ec\u6211\u4eec\u671f\u671b\u7684\u6240\u6709\u7ba1\u9053\u4ee3\u7801\u3002\u8f93\u51fa\u4e0a\u6709\u4e00\u4e2a\u663e\u7740\u7684\u533a\u522b\uff1aToString\uff08\uff09 \u65b9\u6cd5\u9002\u7528\u4e8e\u8bb0\u5f55\u7c7b\u578b\uff0c\u5982\u4ee5\u4e0b\u793a\u4f8b\u8f93\u51fa\u6240\u793a\uff1a<\/p>\n<p><strong><strong><strong><strong><strong><strong><strong><em> RECORDS <\/em><strong>**<\/strong><\/strong><\/strong><\/strong><\/strong><\/strong><\/strong><\/strong> My car:<br \/>\nCarRecord { Make = Honda, Model = Pilot, Color = Blue } Another variable for my car:<br \/>\nCarRecord { Make = Honda, Model = Pilot, Color = Blue }<\/p>\n<h2>Immutable Record Types with Positional Syntax<\/h2>\n<p>\u5177\u6709\u4f4d\u7f6e\u8bed\u6cd5\u7684\u4e0d\u53ef\u53d8\u8bb0\u5f55\u7c7b\u578b<\/p>\n<p>Consider this updated (and much abbreviated) definition for the Car record:<br \/>\n\u8003\u8651\u4e00\u4e0b\u6c7d\u8f66\u8bb0\u5f55\u7684\u66f4\u65b0\uff08\u548c\u7f29\u5199\uff09\u5b9a\u4e49\uff1a<\/p>\n<p>record CarRecord(string Make, string Model, string Color);<\/p>\n<p>Referred to as a positional record type, the constructor defines the properties on the record, and all of the other plumbing code has been removed. There are three considerations when using this syntax: the first is that you cannot use object initialization of record types using the compact definition syntax, the second is that the record must be constructed with the properties in the correct position, and the third is that the casing of the properties in the constructor is directly translated to the casing of the properties on the record type.<\/p>\n<p>\u6784\u9020\u51fd\u6570\u79f0\u4e3a\u4f4d\u7f6e\u8bb0\u5f55\u7c7b\u578b\uff0c\u5b83\u5b9a\u4e49\u8bb0\u5f55\u4e0a\u7684\u5c5e\u6027\uff0c\u5e76\u4e14\u5df2\u5220\u9664\u6240\u6709\u5176\u4ed6\u7ba1\u9053\u4ee3\u7801\u3002\u4f7f\u7528\u6b64\u8bed\u6cd5\u65f6\u6709\u4e09\u4e2a\u6ce8\u610f\u4e8b\u9879\uff1a\u7b2c\u4e00\u4e2a\u662f\u4e0d\u80fd\u4f7f\u7528\u7d27\u51d1\u5b9a\u4e49\u8bed\u6cd5\u5bf9\u8bb0\u5f55\u7c7b\u578b\u4f7f\u7528\u5bf9\u8c61\u521d\u59cb\u5316\uff0c\u7b2c\u4e8c\u4e2a\u662f\u5fc5\u987b\u4f7f\u7528\u4f4d\u4e8e\u6b63\u786e\u4f4d\u7f6e\u7684\u5c5e\u6027\u6784\u9020\u8bb0\u5f55\uff0c\u7b2c\u4e09\u4e2a\u662f\u6784\u9020\u51fd\u6570\u4e2d\u5c5e\u6027\u7684\u5927\u5c0f\u5199\u76f4\u63a5\u8f6c\u6362\u4e3a\u8bb0\u5f55\u7c7b\u578b\u4e0a\u5c5e\u6027\u7684\u5927\u5c0f\u5199\u3002<br \/>\nWe can confirm that the Make, Model, and Color are all init-only properties on the Car record by looking at an abbreviated listing of the IL. Notice that there are backing fields for each of the parameters passed into the constructor, and each has the private and initonly modifiers.<br \/>\n\u6211\u4eec\u53ef\u4ee5\u901a\u8fc7\u67e5\u770b IL \u7684\u7f29\u5199\u5217\u8868\u6765\u786e\u8ba4 Make\u3001Model \u548c Color \u90fd\u662f Car \u8bb0\u5f55\u4e0a\u7684\u4ec5\u521d\u59cb\u5316\u5c5e\u6027\u3002 \u8bf7\u6ce8\u610f\uff0c\u4f20\u9012\u7ed9\u6784\u9020\u51fd\u6570\u7684\u6bcf\u4e2a\u53c2\u6570\u90fd\u6709\u652f\u6301\u5b57\u6bb5\uff0c\u5e76\u4e14\u6bcf\u4e2a\u53c2\u6570\u90fd\u6709\u79c1\u6709\u548c initonly \u4fee\u9970\u7b26\u3002<\/p>\n<p>.class private auto ansi beforefieldinit FunWithRecords.CarRecord extends [System.Runtime]System.Object<br \/>\nimplements class [System.Runtime]System.IEquatable`1<class FunWithRecords.CarRecord><br \/>\n{<br \/>\n.field private initonly string '<Make>k BackingField'<br \/>\n.field private initonly string '<Model>k BackingField'<br \/>\n.field private initonly string '<Color>k BackingField'<br \/>\n...<br \/>\n}<\/p>\n<p>When using the positional syntax, record types provide a primary constructor that matches the positional parameters on the record declaration.<br \/>\n\u4f7f\u7528\u4f4d\u7f6e\u8bed\u6cd5\u65f6\uff0c\u8bb0\u5f55\u7c7b\u578b\u63d0\u4f9b\u4e0e\u8bb0\u5f55\u58f0\u660e\u4e0a\u7684\u4f4d\u7f6e\u53c2\u6570\u5339\u914d\u7684\u4e3b\u6784\u9020\u51fd\u6570\u3002<\/p>\n<h2>Deconstructing Mutable Record Types<\/h2>\n<p>\u89e3\u6784\u53ef\u53d8\u8bb0\u5f55\u7c7b\u578b<\/p>\n<p>Record types using positional parameters also provide a Deconstruct() method with an out parameter for each positional parameter in the declaration. The following code creates a new record using the supplied constructor and then deconstructs the properties into separate variables:<br \/>\n\u4f7f\u7528\u4f4d\u7f6e\u53c2\u6570\u7684\u8bb0\u5f55\u7c7b\u578b\u8fd8\u63d0\u4f9b\u4e00\u4e2a Deconstruct\uff08\uff09 \u65b9\u6cd5\uff0c\u8be5\u65b9\u6cd5\u4e3a\u58f0\u660e\u4e2d\u7684\u6bcf\u4e2a\u4f4d\u7f6e\u53c2\u6570\u63d0\u4f9b\u4e00\u4e2a out \u53c2\u6570\u3002\u4e0b\u9762\u7684\u4ee3\u7801\u4f7f\u7528\u63d0\u4f9b\u7684\u6784\u9020\u51fd\u6570\u521b\u5efa\u65b0\u8bb0\u5f55\uff0c\u7136\u540e\u5c06\u5c5e\u6027\u89e3\u6784\u4e3a\u5355\u72ec\u7684\u53d8\u91cf\uff1a<\/p>\n<p>CarRecord myCarRecord = new CarRecord(&quot;Honda&quot;, &quot;Pilot&quot;, &quot;Blue&quot;); myCarRecord.Deconstruct(out string make, out string model, out string color); Console.WriteLine($&quot;Make: {make} Model: {model} Color: {color}&quot;);<\/p>\n<p>Note that while the public properties on the record match the casing of the declaration, the out variables in the Deconstruct() method only have to match the position of the parameters. Changing the names of the variables in the Deconstruct() method still returns Make, Model, and Color, in that order:<br \/>\n\u8bf7\u6ce8\u610f\uff0c\u867d\u7136\u8bb0\u5f55\u4e0a\u7684\u516c\u5171\u5c5e\u6027\u4e0e\u58f0\u660e\u7684\u5927\u5c0f\u5199\u5339\u914d\uff0c\u4f46 Deconstruct\uff08\uff09 \u65b9\u6cd5\u4e2d\u7684 out \u53d8\u91cf\u53ea\u9700\u8981\u4e0e\u53c2\u6570\u7684\u4f4d\u7f6e\u5339\u914d\u3002\u5728 Deconstruct\uff08\uff09 \u65b9\u6cd5\u4e2d\u66f4\u6539\u53d8\u91cf\u7684\u540d\u79f0\u4ecd\u6309\u8be5\u987a\u5e8f\u8fd4\u56de Make\u3001Model \u548c Color\uff1a<\/p>\n<p>myCarRecord.Deconstruct(out string a, out string b, out string c); Console.WriteLine($&quot;Make: {a} Model: {b} Color: {c}&quot;);<\/p>\n<p>The tuple syntax can also be used when deconstructing records. Note the following addition to the example:<br \/>\n\u89e3\u6784\u8bb0\u5f55\u65f6\u4e5f\u53ef\u4ee5\u4f7f\u7528\u5143\u7ec4\u8bed\u6cd5\u3002\u8bf7\u6ce8\u610f\u793a\u4f8b\u4e2d\u7684\u4ee5\u4e0b\u8865\u5145\uff1a<\/p>\n<p>var (make2, model2, color2) = myCarRecord;<br \/>\nConsole.WriteLine($&quot;Make: {make2} Model: {model2} Color: {color2}&quot;);<\/p>\n<h2>Mutable Record Types<\/h2>\n<p>\u53ef\u53d8\u8bb0\u5f55\u7c7b\u578b<br \/>\nC# also supports mutable record types by using standard (not init-only) setters. The following is an example of this:<br \/>\nC# \u8fd8\u901a\u8fc7\u4f7f\u7528\u6807\u51c6\uff08\u4e0d\u662f\u4ec5\u521d\u59cb\u5316\uff09\u8d44\u6e90\u5e93\u6765\u652f\u6301\u53ef\u53d8\u8bb0\u5f55\u7c7b\u578b\u3002\u4e0b\u9762\u662f\u4e00\u4e2a\u793a\u4f8b\uff1a<\/p>\n<p>record CarRecord<br \/>\n{<br \/>\npublic string Make { get; set; } public string Model { get; set; } public string Color { get; set; }<\/p>\n<p>public CarRecord() {}<br \/>\npublic CarRecord(string make, string model, string color)<br \/>\n{<br \/>\nMake = make;<br \/>\nModel = model;<br \/>\nColor = color;<br \/>\n}<br \/>\n}<\/p>\n<p>While this syntax is supported, the record types are intended to be used for immutable data models.<br \/>\n\u867d\u7136\u652f\u6301\u6b64\u8bed\u6cd5\uff0c\u4f46\u8bb0\u5f55\u7c7b\u578b\u65e8\u5728\u7528\u4e8e\u4e0d\u53ef\u53d8\u6570\u636e\u6a21\u578b\u3002<\/p>\n<h2>Value Equality with Record Types<\/h2>\n<p>\u8bb0\u5f55\u7c7b\u578b\u7684\u503c\u76f8\u7b49<\/p>\n<p>In the Car class example, the two Car instances were created with the same data. One might think that these two classes are equal, as the following line of code tests:<br \/>\n\u5728 Car \u7c7b\u793a\u4f8b\u4e2d\uff0c\u4e24\u4e2a Car \u5b9e\u4f8b\u662f\u4f7f\u7528\u76f8\u540c\u7684\u6570\u636e\u521b\u5efa\u7684\u3002\u6709\u4eba\u53ef\u80fd\u4f1a\u8ba4\u4e3a\u8fd9\u4e24\u4e2a\u7c7b\u662f\u76f8\u7b49\u7684\uff0c\u56e0\u4e3a\u4e0b\u9762\u7684\u4ee3\u7801\u884c\u6d4b\u8bd5\uff1a<\/p>\n<p>Console.WriteLine($&quot;Cars are the same? {myCar.Equals(anotherMyCar)}&quot;);<\/p>\n<p>However, they are not equal. Recall that record types are a specialized type of class, and classes are reference types. For two reference types to be equal, they have to point to the same object in memory. As a further test, check to see if the two Car objects point to the same object:<br \/>\n\u4f46\u662f\uff0c\u5b83\u4eec\u5e76\u4e0d\u76f8\u7b49\u3002\u56de\u60f3\u4e00\u4e0b\uff0c\u8bb0\u5f55\u7c7b\u578b\u662f\u7c7b\u7684\u4e13\u7528\u7c7b\u578b\uff0c\u7c7b\u662f\u5f15\u7528\u7c7b\u578b\u3002\u8981\u4f7f\u4e24\u4e2a\u5f15\u7528\u7c7b\u578b\u76f8\u7b49\uff0c\u5b83\u4eec\u5fc5\u987b\u6307\u5411\u5185\u5b58\u4e2d\u7684\u540c\u4e00\u5bf9\u8c61\u3002\u4f5c\u4e3a\u8fdb\u4e00\u6b65\u7684\u6d4b\u8bd5\uff0c\u8bf7\u68c0\u67e5\u4e24\u4e2a Car \u5bf9\u8c61\u662f\u5426\u6307\u5411\u540c\u4e00\u5bf9\u8c61\uff1a<\/p>\n<p>Console.WriteLine($&quot;Cars are the same reference? {ReferenceEquals(myCar, anotherMyCar)}&quot;);<\/p>\n<p>Running the program again produces this (abbreviated) result:<br \/>\n\u518d\u6b21\u8fd0\u884c\u7a0b\u5e8f\u4f1a\u4ea7\u751f\u4ee5\u4e0b\uff08\u7f29\u5199\uff09\u7ed3\u679c\uff1a<\/p>\n<p>Cars are the same? False CarRecords are the same? False<\/p>\n<p>Record types behave differently. Record types implicitly override Equals, ==, and !=, and two record types are considered equal if the hold the same values and are the same type, just as if the instances are value types. Consider the following code and the subsequent results:<br \/>\n\u8bb0\u5f55\u7c7b\u578b\u7684\u884c\u4e3a\u4e0d\u540c\u3002\u8bb0\u5f55\u7c7b\u578b\u9690\u5f0f\u8986\u76d6 Equals\u3001== \u548c \uff01=\uff0c\u5982\u679c\u4e24\u4e2a\u8bb0\u5f55\u7c7b\u578b\u4fdd\u5b58\u76f8\u540c\u7684\u503c\u5e76\u4e14\u662f\u76f8\u540c\u7684\u7c7b\u578b\uff0c\u5219\u8ba4\u4e3a\u8fd9\u4e24\u4e2a\u8bb0\u5f55\u7c7b\u578b\u76f8\u7b49\uff0c\u5c31\u50cf\u5b9e\u4f8b\u662f\u503c\u7c7b\u578b\u4e00\u6837\u3002\u8bf7\u8003\u8651\u4ee5\u4e0b\u4ee3\u7801\u548c\u540e\u7eed\u7ed3\u679c\uff1a<\/p>\n<p>Console.WriteLine($&quot;CarRecords are the same? {myCarRecord.Equals(anotherMyCarRecord)}&quot;); Console.WriteLine($&quot;CarRecords are the same reference? {ReferenceEquals(myCarRecord,another MyCarRecord)}&quot;);<br \/>\nConsole.WriteLine($&quot;CarRecords are the same? {myCarRecord == anotherMyCarRecord}&quot;); Console.WriteLine($&quot;CarRecords are not the same? {myCarRecord != anotherMyCarRecord}&quot;);<\/p>\n<p>\/<strong><strong><strong><strong><strong><strong><strong><em> RECORDS <\/em><strong>**<\/strong><\/strong><\/strong><\/strong><\/strong><\/strong><\/strong><\/strong>\/ My car:<br \/>\nCarRecord { Make = Honda, Model = Pilot, Color = Blue } Another variable for my car:<br \/>\nCarRecord { Make = Honda, Model = Pilot, Color = Blue }<\/p>\n<p>CarRecords are the same? True<br \/>\nCarRecords are the same reference? false CarRecords are the same? True<br \/>\nCarRecords are not the same? False<\/p>\n<p>Notice that they are considered equal, even though the variables point to two different variables in memory.<br \/>\n\u8bf7\u6ce8\u610f\uff0c\u5373\u4f7f\u53d8\u91cf\u6307\u5411\u5185\u5b58\u4e2d\u7684\u4e24\u4e2a\u4e0d\u540c\u53d8\u91cf\uff0c\u5b83\u4eec\u4e5f\u88ab\u89c6\u4e3a\u76f8\u7b49\u3002<\/p>\n<h2>Copying Record Types Using with Expressions<\/h2>\n<p>\u590d\u5236\u8bb0\u5f55\u7c7b\u578b \u4e0e\u8868\u8fbe\u5f0f\u4e00\u8d77\u4f7f\u7528<\/p>\n<p>With record types, assigning a record type instance to a new variable creates a pointer to the same reference, which is the same behavior as classes. The following code demonstrates this:<br \/>\n\u5bf9\u4e8e\u8bb0\u5f55\u7c7b\u578b\uff0c\u5c06\u8bb0\u5f55\u7c7b\u578b\u5b9e\u4f8b\u5206\u914d\u7ed9\u65b0\u53d8\u91cf\u4f1a\u521b\u5efa\u4e00\u4e2a\u6307\u5411\u540c\u4e00\u5f15\u7528\u7684\u6307\u9488\uff0c\u8fd9\u4e0e\u7c7b\u7684\u884c\u4e3a\u76f8\u540c\u3002\u4ee5\u4e0b\u4ee3\u7801\u5bf9\u6b64\u8fdb\u884c\u4e86\u6f14\u793a\uff1a<\/p>\n<p>CarRecord carRecordCopy = anotherMyCarRecord; Console.WriteLine(&quot;Car Record copy results&quot;);<br \/>\nConsole.WriteLine($&quot;CarRecords are the same? {carRecordCopy.Equals(anotherMyCarRecord)}&quot;); Console.WriteLine($&quot;CarRecords are the same? {ReferenceEquals(carRecordCopy, anotherMyCarRecord)}&quot;);<\/p>\n<p>When executed, both tests return true, proving that they are the same in value and reference.<br \/>\n\u6267\u884c\u65f6\uff0c\u4e24\u4e2a\u6d4b\u8bd5\u90fd\u8fd4\u56de true\uff0c\u8bc1\u660e\u5b83\u4eec\u5728\u503c\u548c\u5f15\u7528\u4e0a\u76f8\u540c\u3002<\/p>\n<p>To create a true copy of a record with one or more properties modified (referred to as nondestructive mutation), C# 9.0 introduces with expressions. In the with construct, any properties that need to be updated are specified with their new values, and any properties not listed are shallow copied exactly. Examine the following example:<br \/>\n\u82e5\u8981\u521b\u5efa\u4fee\u6539\u4e86\u4e00\u4e2a\u6216\u591a\u4e2a\u5c5e\u6027\uff08\u79f0\u4e3a\u975e\u7834\u574f\u6027\u7a81\u53d8\uff09\u7684\u8bb0\u5f55\u7684\u771f\u5b9e\u526f\u672c\uff0cC# 9.0 \u5f15\u5165\u4e86\u8868\u8fbe\u5f0f\u3002 \u5728 with \u6784\u9020\u4e2d\uff0c\u9700\u8981\u66f4\u65b0\u7684\u4efb\u4f55\u5c5e\u6027\u90fd\u4f7f\u7528\u5176\u65b0\u503c\u6307\u5b9a\uff0c\u5e76\u4e14\u672a\u5217\u51fa\u7684\u4efb\u4f55\u5c5e\u6027\u90fd\u662f\u5b8c\u5168\u6d45\u62f7\u8d1d\u7684\u3002\u68c0\u67e5\u4ee5\u4e0b\u793a\u4f8b\uff1a<\/p>\n<p>CarRecord ourOtherCar = myCarRecord with {Model = &quot;Odyssey&quot;}; Console.WriteLine(&quot;My copied car:&quot;); Console.WriteLine(ourOtherCar.ToString());<\/p>\n<p>Console.WriteLine(&quot;Car Record copy using with expression results&quot;); Console.WriteLine($&quot;CarRecords are the same? {ourOtherCar.Equals(myCarRecord)}&quot;); Console.WriteLine($&quot;CarRecords are the same? {ReferenceEquals(ourOtherCar, myCarRecord)}&quot;);<\/p>\n<p>The code creates a new instance of the CarRecord type, copying the Make and Color values of the<br \/>\nmyCarRecord instance and setting Model to the string Odyssey. The results of this code is shown here:<br \/>\n\u8be5\u4ee3\u7801\u521b\u5efa CarRecord \u7c7b\u578b\u7684\u65b0\u5b9e\u4f8b\uff0c\u590d\u5236myCarRecord \u5b9e\u4f8b\u5e76\u5c06\u6a21\u578b\u8bbe\u7f6e\u4e3a\u5b57\u7b26\u4e32 Odyssey\u3002\u6b64\u4ee3\u7801\u7684\u7ed3\u679c\u5982\u4e0b\u6240\u793a\uff1a<\/p>\n<p>\/<strong><strong><strong><strong><strong><strong><strong><em> RECORDS <\/em><strong>**<\/strong><\/strong><\/strong><\/strong><\/strong><\/strong><\/strong><\/strong>\/ My copied car:<br \/>\nCarRecord { Make = Honda, Model = Odyssey, Color = Blue }<\/p>\n<p>Car Record copy using with expression results CarRecords are the same? False<br \/>\nCarRecords are the same? False<\/p>\n<p>Using with expressions, you can easily take complex record types into new record type instances with updated property values.<br \/>\n\u4f7f\u7528 with \u8868\u8fbe\u5f0f\uff0c\u60a8\u53ef\u4ee5\u8f7b\u677e\u5730\u5c06\u590d\u6742\u7684\u8bb0\u5f55\u7c7b\u578b\u653e\u5165\u5177\u6709\u66f4\u65b0\u5c5e\u6027\u503c\u7684\u65b0\u8bb0\u5f55\u7c7b\u578b\u5b9e\u4f8b\u4e2d\u3002<\/p>\n<h2>Record Structs (New 10.0)<\/h2>\n<p>\u8bb0\u5f55\u7ed3\u6784\uff08\u65b0 10.0\uff09<\/p>\n<p>New in C# 10.0, record structs are the value type equivalent of record types. Record structs can also use positional parameters or standard property syntax and provide value equality, nondestructive mutation, and built-in display formatting. To start experimenting with records, create a new console application named FunWithRecordStructs. One major difference between record structs and records is that a record struct is mutable by default. To make a record struct immutable, you must use add the readonly modifier.<br \/>\n\u4f5c\u4e3a C# 10.0 \u4e2d\u7684\u65b0\u589e\u529f\u80fd\uff0c\u8bb0\u5f55\u7ed3\u6784\u662f\u7b49\u6548\u4e8e\u8bb0\u5f55\u7c7b\u578b\u7684\u503c\u7c7b\u578b\u3002\u8bb0\u5f55\u7ed3\u6784\u8fd8\u53ef\u4ee5\u4f7f\u7528\u4f4d\u7f6e\u53c2\u6570\u6216\u6807\u51c6\u5c5e\u6027\u8bed\u6cd5\uff0c\u5e76\u63d0\u4f9b\u503c\u76f8\u7b49\u3001\u975e\u7834\u574f\u6027\u7a81\u53d8\u548c\u5185\u7f6e\u663e\u793a\u683c\u5f0f\u3002\u82e5\u8981\u5f00\u59cb\u8bd5\u9a8c\u8bb0\u5f55\uff0c\u8bf7\u521b\u5efa\u4e00\u4e2a\u540d\u4e3a FunWithRecordStructs \u7684\u65b0\u63a7\u5236\u53f0\u5e94\u7528\u7a0b\u5e8f\u3002\u8bb0\u5f55\u7ed3\u6784\u548c\u8bb0\u5f55\u4e4b\u95f4\u7684\u4e00\u4e2a\u4e3b\u8981\u533a\u522b\u662f\uff0c\u9ed8\u8ba4\u60c5\u51b5\u4e0b\uff0c\u8bb0\u5f55\u7ed3\u6784\u662f\u53ef\u53d8\u7684\u3002\u82e5\u8981\u4f7f\u8bb0\u5f55\u7ed3\u6784\u4e0d\u53ef\u53d8\uff0c\u5fc5\u987b\u4f7f\u7528\u6dfb\u52a0\u53ea\u8bfb\u4fee\u9970\u7b26\u3002<\/p>\n<h2>Mutable Record Structs<\/h2>\n<p>\u53ef\u53d8\u8bb0\u5f55\u7ed3\u6784<\/p>\n<p>To create a record struct, let\u2019s revisit our friend the Point struct. The following shows how to create two different record struct types, one using the positional syntax and the other using standard properties:<br \/>\n\u8981\u521b\u5efa\u4e00\u4e2a\u8bb0\u5f55\u7ed3\u6784\uff0c\u8ba9\u6211\u4eec\u91cd\u65b0\u8bbf\u95ee\u6211\u4eec\u7684\u670b\u53cb Point \u7ed3\u6784\u3002\u4e0b\u9762\u6f14\u793a\u5982\u4f55\u521b\u5efa\u4e24\u79cd\u4e0d\u540c\u7684\u8bb0\u5f55\u7ed3\u6784\u7c7b\u578b\uff0c\u4e00\u79cd\u4f7f\u7528\u4f4d\u7f6e\u8bed\u6cd5\uff0c\u53e6\u4e00\u79cd\u4f7f\u7528\u6807\u51c6\u5c5e\u6027\uff1a<\/p>\n<p>public record struct Point(double X, double Y, double Z); public record struct PointWithPropertySyntax()<br \/>\n{<br \/>\npublic double X { get; set; } = default; public double Y { get; set; } = default; public double Z { get; set; } = default;<\/p>\n<p>public PointWithPropertySyntax(double x, double y, double z) : this()<br \/>\n{<br \/>\nX = x; Y = y; Z = z;<br \/>\n}<br \/>\n};<\/p>\n<p>The following code demonstrates the mutability of the two record struct types as well as the improved ToString() method:<br \/>\n\u4e0b\u9762\u7684\u4ee3\u7801\u6f14\u793a\u4e86\u4e24\u79cd\u8bb0\u5f55\u7ed3\u6784\u7c7b\u578b\u7684\u53ef\u53d8\u6027\u4ee5\u53ca\u6539\u8fdb\u7684ToString\uff08\uff09 \u65b9\u6cd5\uff1a<\/p>\n<p>Console.WriteLine(&quot;<strong><strong><em> Fun With Record Structs <\/em><\/strong><\/strong>&quot;); var rs = new Point(2, 4, 6); Console.WriteLine(rs.ToString());<br \/>\nrs.X = 8;<br \/>\nConsole.WriteLine(rs.ToString());<\/p>\n<p>var rs2 = new PointWithPropertySyntax(2, 4, 6); Console.WriteLine(rs2.ToString());<br \/>\nrs2.X = 8;<br \/>\nConsole.WriteLine(rs2.ToString());<\/p>\n<h2>Immutable Record Structs<\/h2>\n<p>\u4e0d\u53ef\u53d8\u7684\u8bb0\u5f55\u7ed3\u6784<\/p>\n<p>The previous two record struct examples can be made immutable by adding the readonly keyword:<br \/>\n\u524d\u4e24\u4e2a\u8bb0\u5f55\u7ed3\u6784\u793a\u4f8b\u53ef\u4ee5\u901a\u8fc7\u6dfb\u52a0\u53ea\u8bfb\u5173\u952e\u5b57\u4f7f\u5b83\u4eec\u4e0d\u53ef\u53d8\uff1a<\/p>\n<p>public readonly record struct ReadOnlyPoint(double X, double Y, double Z); public readonly record struct ReadOnlyPointWithPropertySyntax()<br \/>\n{<br \/>\npublic double X { get; init; } = default; public double Y { get; init; } = default; public double Z { get; init; } = default;<br \/>\npublic ReadOnlyPointWithPropertySyntax(double x, double y, double z) : this()<br \/>\n{<br \/>\nX = x; Y = y; Z = z;<br \/>\n}<br \/>\n};<\/p>\n<p>You can confirm that they are now immutable (enforced by the compiler) with the following code:<br \/>\n\u60a8\u53ef\u4ee5\u4f7f\u7528\u4ee5\u4e0b\u4ee3\u7801\u786e\u8ba4\u5b83\u4eec\u73b0\u5728\u662f\u4e0d\u53ef\u53d8\u7684\uff08\u7531\u7f16\u8bd1\u5668\u5f3a\u5236\u6267\u884c\uff09\uff1a<\/p>\n<p>var rors = new ReadOnlyPoint(2, 4, 6);<br \/>\n\/\/Compiler Error:<br \/>\n\/\/rors.X = 8;<\/p>\n<p>var rors2 = new ReadOnlyPointWithPropertySyntax(2, 4, 6);<br \/>\n\/\/Compiler Error:<br \/>\n\/\/rors2.X = 8;<\/p>\n<p>Value equality and copying using with expressions work the same as record types.<br \/>\n\u503c\u76f8\u7b49\u548c\u4e0e\u8868\u8fbe\u5f0f\u4e00\u8d77\u4f7f\u7528\u7684\u590d\u5236\u4e0e\u8bb0\u5f55\u7c7b\u578b\u7684\u5de5\u4f5c\u65b9\u5f0f\u76f8\u540c\u3002<\/p>\n<h2>Deconstructing Record Structs<\/h2>\n<p>\u89e3\u6784\u8bb0\u5f55\u7ed3\u6784<\/p>\n<p>Like record types, record structs that use positional syntax also provide a Deconstruct() method. The behavior is the same as mutable and immutable record structs. The following code creates a new record using the supplied constructor and then deconstructs the properties into separate variables:<br \/>\n\u4e0e\u8bb0\u5f55\u7c7b\u578b\u4e00\u6837\uff0c\u4f7f\u7528\u4f4d\u7f6e\u8bed\u6cd5\u7684\u8bb0\u5f55\u7ed3\u6784\u4e5f\u63d0\u4f9b Deconstruct\uff08\uff09 \u65b9\u6cd5\u3002\u8be5\u884c\u4e3a\u4e0e\u53ef\u53d8\u548c\u4e0d\u53ef\u53d8\u8bb0\u5f55\u7ed3\u6784\u76f8\u540c\u3002\u4e0b\u9762\u7684\u4ee3\u7801\u4f7f\u7528\u63d0\u4f9b\u7684\u6784\u9020\u51fd\u6570\u521b\u5efa\u65b0\u8bb0\u5f55\uff0c\u7136\u540e\u5c06\u5c5e\u6027\u89e3\u6784\u4e3a\u5355\u72ec\u7684\u53d8\u91cf\uff1a<\/p>\n<p>Console.WriteLine(&quot;Deconstruction: &quot;); var (x1, y1, z1) = rs;<br \/>\nConsole.WriteLine($&quot;X: {x1} Y: {y1} Z: {z1}&quot;); var (x2, y2, z2) = rors; Console.WriteLine($&quot;X: {x2} Y: {y2} Z: {z2}&quot;);<br \/>\nrs.Deconstruct(out double x3,out double y3,out double z3); Console.WriteLine($&quot;X: {x3} Y: {y3} Z: {z3}&quot;); rors.Deconstruct(out double x4,out double y4,out double z4); Console.WriteLine($&quot;X: {x4} Y: {y4} Z: {z4}&quot;);<\/p>\n<p>Summary<br \/>\n\u603b\u7ed3<\/p>\n<p>The point of this chapter was to introduce you to the role of the C# class type and the new C# 9.0 record type. As you have seen, classes can take any number of constructors that enable the object user to establish the state of the object upon creation. This chapter also illustrated several class design techniques (and related keywords). The this keyword can be used to obtain access to the current object. The static keyword allows you to define fields and members that are bound at the class (not object) level. The const keyword, readonly modifier, and init-only setters allow you to define a point of data that can never change after the initial assignment or object construction. Record types are a special type of class that are immutable (by default) and behave like value types when comparing a record type with another instance of the same record type. Record structs are value types that are mutable (by default) and provide the same equality and nondestructive mutation capabilities as record types.<br \/>\n\u672c\u7ae0\u7684\u91cd\u70b9\u662f\u5411\u60a8\u4ecb\u7ecd C# \u7c7b\u7c7b\u578b\u548c\u65b0\u7684 C# 9.0 \u8bb0\u5f55\u7c7b\u578b\u7684\u4f5c\u7528\u3002\u5982\u60a8\u6240\u89c1\uff0c\u7c7b\u53ef\u4ee5\u91c7\u7528\u4efb\u610f\u6570\u91cf\u7684\u6784\u9020\u51fd\u6570\uff0c\u8fd9\u4e9b\u6784\u9020\u51fd\u6570\u4f7f\u5bf9\u8c61\u7528\u6237\u80fd\u591f\u5728\u521b\u5efa\u65f6\u5efa\u7acb\u5bf9\u8c61\u7684\u72b6\u6001\u3002\u672c\u7ae0\u8fd8\u8bf4\u660e\u4e86\u51e0\u79cd\u7c7b\u8bbe\u8ba1\u6280\u672f\uff08\u548c\u76f8\u5173\u5173\u952e\u5b57\uff09\u3002this \u5173\u952e\u5b57\u53ef\u7528\u4e8e\u83b7\u53d6\u5bf9\u5f53\u524d\u5bf9\u8c61\u7684\u8bbf\u95ee\u6743\u9650\u3002\u9759\u6001\u5173\u952e\u5b57\u5141\u8bb8\u60a8\u5b9a\u4e49\u7ed1\u5b9a\u5728\u7c7b\uff08\u800c\u975e\u5bf9\u8c61\uff09\u7ea7\u522b\u7684\u5b57\u6bb5\u548c\u6210\u5458\u3002const \u5173\u952e\u5b57\u3001\u53ea\u8bfb\u4fee\u9970\u7b26\u548c\u4ec5\u521d\u59cb\u5316\u8d44\u6e90\u5e93\u5141\u8bb8\u60a8\u5b9a\u4e49\u5728\u521d\u59cb\u8d4b\u503c\u6216\u5bf9\u8c61\u6784\u9020\u540e\u6c38\u8fdc\u4e0d\u4f1a\u66f4\u6539\u7684\u6570\u636e\u70b9\u3002\u8bb0\u5f55\u7c7b\u578b\u662f\u4e0d\u53ef\u53d8\u7684\u7279\u6b8a\u7c7b\u578b\u7684\u7c7b\uff08\u9ed8\u8ba4\u60c5\u51b5\u4e0b\uff09\u5e76\u4e14\u5728\u5c06\u8bb0\u5f55\u7c7b\u578b\u4e0e\u540c\u4e00\u8bb0\u5f55\u7c7b\u578b\u7684\u53e6\u4e00\u4e2a\u5b9e\u4f8b\u8fdb\u884c\u6bd4\u8f83\u65f6\u7684\u884c\u4e3a\u7c7b\u4f3c\u4e8e\u503c\u7c7b\u578b\u3002\u8bb0\u5f55\u7ed3\u6784\u662f\u53ef\u53d8\u7684\u503c\u7c7b\u578b\uff08\u9ed8\u8ba4\u60c5\u51b5\u4e0b\uff09\uff0c\u5e76\u63d0\u4f9b\u4e0e\u8bb0\u5f55\u7c7b\u578b\u76f8\u540c\u7684\u76f8\u7b49\u548c\u975e\u7834\u574f\u6027\u7a81\u53d8\u529f\u80fd\u3002<\/p>\n<p>The bulk of this chapter dug into the details of the first pillar of OOP: encapsulation. You learned about the access modifiers of C# and the role of type properties, object initialization syntax, and partial classes.<br \/>\n\u672c\u7ae0\u7684\u5927\u90e8\u5206\u5185\u5bb9\u6df1\u5165\u63a2\u8ba8\u4e86 OOP \u7684\u7b2c\u4e00\u4e2a\u652f\u67f1\uff1a\u5c01\u88c5\u3002\u60a8\u4e86\u89e3\u4e86 C# \u7684\u8bbf\u95ee\u4fee\u9970\u7b26\u4ee5\u53ca\u7c7b\u578b\u5c5e\u6027\u3001\u5bf9\u8c61\u521d\u59cb\u5316\u8bed\u6cd5\u548c\u5206\u90e8\u7c7b\u7684\u4f5c\u7528\u3002<\/p>\n<p>With this behind you, you are now able to turn to the next chapter where you will learn to build a family of related classes using inheritance and polymorphism.<br \/>\n\u6709\u4e86\u8fd9\u4e2a\uff0c\u4f60\u73b0\u5728\u53ef\u4ee5\u8fdb\u5165\u4e0b\u4e00\u7ae0\uff0c\u4f60\u5c06\u5b66\u4e60\u4f7f\u7528\u7ee7\u627f\u548c\u591a\u6001\u6027\u6784\u5efa\u4e00\u4e2a\u76f8\u5173\u7c7b\u7684\u5bb6\u65cf\u3002<\/p>\n","protected":false},"excerpt":{"rendered":"<p>PART III Object Oriented Programming with C \u7b2c\u4e09\u90e8\u5206 \u4f7f\u7528 C \u8fdb\u884c\u9762\u5411\u5bf9\u8c61\u7f16\u7a0b# CHAPTER 5 Understanding Encapsulation \u4e86\u89e3\u5c01\u88c5 In Chapters 3 and 4, you investigated a number of core syntactical constructs that are commonplace to any .NET Core application you might be developing. Here, you will begin your examination of the object-oriented capabilities of C#. The first [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[3],"tags":[22],"class_list":["post-295","post","type-post","status-publish","format-standard","hentry","category-csharp","tag-pro-csharp10-with-net6"],"_links":{"self":[{"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/posts\/295","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=295"}],"version-history":[{"count":0,"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/posts\/295\/revisions"}],"wp:attachment":[{"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=295"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=295"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=295"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}