{"id":292,"date":"2023-10-20T13:17:33","date_gmt":"2023-10-20T05:17:33","guid":{"rendered":"https:\/\/miie.net\/?p=292"},"modified":"2023-10-20T13:17:33","modified_gmt":"2023-10-20T05:17:33","slug":"pro-c10-chapter-4-core-c-programming-constructs-part-2","status":"publish","type":"post","link":"https:\/\/diji.net\/?p=292","title":{"rendered":"Pro C#10 CHAPTER 4 Core C# Programming Constructs, Part 2"},"content":{"rendered":"<h2>CHAPTER 4<\/h2>\n<p>\u7b2c4\u7ae0<\/p>\n<h2>Core C# Programming Constructs, Part 2<\/h2>\n<p>\u6838\u5fc3 C# \u7f16\u7a0b \u6784\u9020\uff0c\u7b2c 2 \u90e8\u5206<\/p>\n<p>This chapter picks up where Chapter 3 left off and completes your investigation of the core aspects of the C# programming language. You will start with an investigation of the details behind manipulating arrays using the syntax of C# and get to know the functionality contained within the related System.Array class type.<br \/>\n\u4ece\u7b2c\u4e09\u7ae0\u7ed3\u675f\u7684\u5730\u65b9\u5f00\u59cb\uff0c\u5b8c\u6210\u5bf9C#\u7f16\u7a0b\u8bed\u8a00\u6838\u5fc3\u65b9\u9762\u7684\u7814\u7a76\u3002\u4eceC#\u64cd\u4f5c\u6570\u636e\u80cc\u540e\u7684\u7ec6\u8282\u5f00\u59cb\uff0c\u4e86\u89e3System.Array\u76f8\u5173\u7684\u529f\u80fd\u3002<\/p>\n<p>Next, you will examine various details regarding the construction of C# methods, exploring the out, ref, and params keywords. Along the way, you will also examine the role of optional and named parameters. I finish the discussion on methods with a look at method overloading.<br \/>\n\u63a5\u4e0b\u6765\uff0c\u7814\u7a76C#\u65b9\u6cd5\u7684\u5404\u79cd\u7ec6\u8282\uff0c\u63a2\u8ba8out\uff0cref\u5173\u952e\u5b57\u548c\u53c2\u6570\u3002\u5728\u6b64\u8fc7\u7a0b\u4e2d\uff0c\u63a2\u8ba8\u53ef\u9009\u53c2\u6570\u548c\u547d\u540d\u53c2\u6570\u7684\u4f5c\u7528\u3002\u6700\u540e\uff0c\u4ee5\u65b9\u6cd5\u91cd\u8f7d\u6765\u7ed3\u675f\u5bf9\u65b9\u6cd5\u7684\u8ba8\u8bba\u3002<\/p>\n<p>Next, this chapter discusses the construction of enumeration and structure types, including a detailed examination of the distinction between a value type and a reference type. This chapter wraps up by examining the role of nullable data types and the related operators.<br \/>\n\u63a5\u4e0b\u6765\uff0c\u8ba8\u8bba\u679a\u4e3e\u548c\u7ed3\u6784\u7c7b\u578b\u7684\u6784\u9020\uff0c\u5305\u62ec\u503c\u7c7b\u578b\u548c\u5f15\u7528\u7c7b\u578b\u4e4b\u95f4\u7684\u533a\u522b\u3002\u6700\u540e\u4ecb\u7ecd\u4e86nullable\u6570\u636e\u7c7b\u578b\u548c\u76f8\u5173\u8fd0\u7b97\u7b26\u7684\u4f5c\u7528\u3002<\/p>\n<p>After you have completed this chapter, you will be in a perfect position to learn the object-oriented capabilities of C#, beginning in Chapter 5.<br \/>\n\u5b8c\u6210\u672c\u7ae0\u540e\uff0c\u4ece\u7b2c5\u7ae0\u5f00\u59cb\u5b66\u4e60\u9762\u5411\u5bf9\u8c61\u529f\u80fd\u3002<\/p>\n<h2>Understanding C# Arrays<\/h2>\n<p>\u4e86\u89e3 C# \u6570\u7ec4<\/p>\n<p>As I would guess you are already aware, an array is a set of data items, accessed using a numerical index. More specifically, an array is a set of contiguous data points of the same type (an array of ints, an array of strings, an array of SportsCars, etc.). Declaring, filling, and accessing an array with C# are all quite straightforward. To illustrate, create a new Console Application project named FunWithArrays that contains a helper method named SimpleArrays(); as follows:<br \/>\n\u6211\u60f3\u60a8\u5df2\u7ecf\u77e5\u9053\u4e86\uff0c\u6570\u7ec4\u662f\u4e00\u7ec4\u6570\u636e\u9879\uff0c\u4f7f\u7528\u6570\u5b57\u7d22\u5f15\u8fdb\u884c\u8bbf\u95ee\u3002\u66f4\u5177\u4f53\u5730\u8bf4\uff0c\u6570\u7ec4\u662f\u4e00\u7ec4\u76f8\u540c\u7c7b\u578b\u7684\u8fde\u7eed\u6570\u636e\u70b9\uff08\u6574\u6570\u6570\u7ec4\u3001\u5b57\u7b26\u4e32\u6570\u7ec4\u3001SportsCars\u6570\u7ec4\u7b49\uff09\u3002\u4f7f\u7528C#\u58f0\u660e\u3001\u586b\u5145\u548c\u8bbf\u95ee\u6570\u7ec4\u90fd\u5f88\u7b80\u5355\u3002\u4e3a\u4e86\u8bf4\u660e\u8fd9\u4e00\u70b9\uff0c\u521b\u5efa\u4e00\u4e2a\u65b0\u63a7\u5236\u53f0\u5e94\u7528\u7a0b\u5e8f\u9879\u76ee\uff0c\u540d\u4e3aFunWithArrays\u3002\u8be5\u9879\u76ee\u5305\u542b\u540d\u4e3aSimpleArrays()\u7684\u8f85\u52a9\u65b9\u6cd5\uff08helper\uff09\u3002\u5982\u4e0b\u6240\u793a\uff1a<\/p>\n<p><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nConsole.WriteLine(&quot;***** Fun with Arrays *****&quot;);\nSimpleArrays();\nConsole.ReadLine();\nstatic void SimpleArrays()\n{\n    Console.WriteLine(&quot;=&gt; Simple Array Creation.&quot;);\n    \/\/ Create and fill an array of 3 integers\n    \/\/ \u521b\u5efa\u4e00\u4e2a\u6570\u636e\uff0c\u586b\u51453\u4e2a\u6574\u6570\n    int&#x5B;] myInts = new int&#x5B;3];\n\n    \/\/ Create a 100 item string array, indexed 0 - 99\n    \/\/ \u521b\u5efa\u4e00\u4e2a100\u9879\u7684\u5b57\u7b26\u4e32\u6570\u7ec4\uff0c\u7d22\u5f15\u4e3a0-99\n    string&#x5B;] booksOnDotNet = new string&#x5B;100];\n\n    Console.WriteLine();\n}\n<\/pre>\n<\/p>\n<p>Look closely at the previous code comments. When declaring a C# array using this syntax, the number used in the array declaration represents the total number of items, not the upper bound. Also note that the lower bound of an array always begins at 0. Thus, when you write int[] myInts = new int[3], you end up with an array holding three elements, indexed at positions 0, 1, and 2.<br \/>\n\u4ed4\u7ec6\u67e5\u770b\u4ee3\u7801\u6ce8\u91ca\u3002\u4f7f\u7528\u6b64\u8bed\u6cd5\u58f0\u660eC#\u6570\u7ec4\u65f6\uff0c\u6570\u7ec4\u58f0\u660e\u4e2d\u4f7f\u7528\u7684\u6570\u5b57\u8868\u793a\u9879\u76ee\u7684\u603b\u6570\uff0c\u800c\u4e0d\u662f\u4e0a\u9650\u3002\u8fd8\u8981\u6ce8\u610f\uff0c\u6570\u7ec4\u7684\u4e0b\u9650(\u7d22\u5f15)\u603b\u662f\u4ece0\u5f00\u59cb\u3002\u56e0\u6b64\uff0c\u5f53\u60a8\u7f16\u5199int[] myInts = new int[3]\u65f6\uff0c\u60a8\u6700\u7ec8\u4f1a\u5f97\u5230\u4e00\u4e2a\u5305\u542b\u4e09\u4e2a\u5143\u7d20\u7684\u6570\u7ec4\uff0c\u8fd9\u4e9b\u5143\u7d20\u5728\u4f4d\u7f6e0\u30011\u548c2\u8fdb\u884c\u7d22\u5f15\u3002<\/p>\n<p>After you have defined an array variable, you are then able to fill the elements index by index, as shown here in the updated SimpleArrays() method:<br \/>\n\u5b9a\u4e49\u4e86\u6570\u7ec4\u53d8\u91cf\u540e\uff0c\u5c31\u53ef\u4ee5\u6309\u7d22\u5f15\u586b\u5145\u5143\u7d20\uff0c\u5982\u66f4\u65b0\u540e\u7684SimpleArrays() \u65b9\u6cd5\u6240\u793a\uff1a<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nConsole.WriteLine(&quot;***** Fun with Arrays *****&quot;);\nSimpleArrays();\n\n\nstatic void SimpleArrays()\n{\n    Console.WriteLine(&quot;=&gt; Simple Array Creation.&quot;);\n    \/\/ Create and fill an array of 3 Integers\n    int&#x5B;] myInts = new int&#x5B;3];\n    myInts&#x5B;0] = 100;\n    myInts&#x5B;1] = 200;\n    myInts&#x5B;2] = 300;\n\n    \/\/ Now print each value.\n    \/\/ \u73b0\u5728\uff0c\u6253\u5370\u6bcf\u4e2a\u5143\u7d20\u7684\u503c\n    foreach (int i in myInts)\n    {\n        Console.WriteLine(i);\n    }\n    Console.WriteLine();\n}\n<\/pre>\n<\/p>\n<blockquote><p>\n\u25a0 Note Do be aware that if you declare an array but do not explicitly fill each index, each item will be set to the default value of the data type (e.g., an array of bools will be set to false or an array of ints will be set to 0).<br \/>\n\u6ce8\u610f \u8bf7\u6ce8\u610f\uff0c\u5982\u679c\u60a8\u58f0\u660e\u4e86\u4e00\u4e2a\u6570\u7ec4\uff0c\u4f46\u6ca1\u6709\u663e\u5f0f\u586b\u5145\u6bcf\u4e2a\u7d22\u5f15\uff0c\u5219\u6bcf\u4e2a\u9879\u90fd\u5c06\u8bbe\u7f6e\u4e3a\u6570\u636e\u7c7b\u578b\u7684\u9ed8\u8ba4\u503c\uff08\u4f8b\u5982\uff0c\u5e03\u5c14\u6570\u7ec4\u5c06\u8bbe\u7f6e\u4e3afalse\uff0cint\u6570\u7ec4\u5c06\u8bbe\u7f6e\u62100\uff09\u3002\n<\/p><\/blockquote>\n<h2>Looking at the C# Array Initialization Syntax<\/h2>\n<p>\u67e5\u770b C# \u6570\u7ec4\u521d\u59cb\u5316\u8bed\u6cd5<\/p>\n<p>In addition to filling an array element by element, you can fill the items of an array using C# array initialization syntax. To do so, specify each array item within the scope of curly brackets ({}). This syntax can be helpful when you are creating an array of a known size and want to quickly specify the initial values. For example, consider the following alternative array declarations:<br \/>\n\u9664\u4e86\u9010\u4e2a\u5143\u7d20\u586b\u5145\u6570\u7ec4\u5143\u7d20\u5916\uff0c\u8fd8\u53ef\u4ee5\u4f7f\u7528C#\u6570\u7ec4\u521d\u59cb\u5316\u8bed\u6cd5\u586b\u5145\u6570\u7ec4\u7684\u9879\u3002\u4e3a\u6b64\uff0c\u8bf7\u6307\u5b9a\u82b1\u62ec\u53f7({})\u8303\u56f4\u5185\u7684\u6bcf\u4e2a\u6570\u7ec4\u9879\u3002\u5f53\u60a8\u521b\u5efa\u4e00\u4e2a\u5df2\u77e5\u5927\u5c0f\u7684\u6570\u7ec4\u5e76\u5e0c\u671b\u5feb\u901f\u6307\u5b9a\u521d\u59cb\u503c\u65f6\uff0c\u6b64\u8bed\u6cd5\u4f1a\u5f88\u6709\u7528\u3002\u4f8b\u5982\uff0c\u8003\u8651\u4ee5\u4e0b\u66ff\u4ee3\u6570\u7ec4\u58f0\u660e\uff1a<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nConsole.WriteLine(&quot;***** Fun with Arrays *****&quot;);\nArrayInitialization();\n\nstatic void ArrayInitialization()\n{\n    Console.WriteLine(&quot;=&gt; Array Initialization.&quot;);\n    \/\/ Array initialization syntax using the new keyword.\n    \/\/ \u4f7f\u7528 new \u5173\u952e\u5b57\u7684\u6570\u7ec4\u521d\u59cb\u5316\u8bed\u6cd5\u3002\n    string&#x5B;] stringArray = new string&#x5B;] { &quot;one&quot;, &quot;two&quot;, &quot;three&quot; };\n    Console.WriteLine(&quot;stringArray has {0} elements&quot;, stringArray.Length);\n\n    \/\/ Array initialization syntax without using the new keyword.\n    \/\/ \u4e0d\u4f7f\u7528 new \u5173\u952e\u5b57\u7684\u6570\u7ec4\u521d\u59cb\u5316\u8bed\u6cd5\u3002\n    bool&#x5B;] boolArray = { false, false, true };\n    Console.WriteLine(&quot;boolArray has {0} elements&quot;, boolArray.Length);\n\n    \/\/ Array initialization with new keyword and size.\n    \/\/ \u4f7f\u7528\u65b0\u7684\u5173\u952e\u5b57\u548c\u5927\u5c0f\u8fdb\u884c\u6570\u7ec4\u521d\u59cb\u5316\u3002\n    int&#x5B;] intArray = new int&#x5B;4] { 20, 22, 23, 0 };\n    Console.WriteLine(&quot;intArray has {0} elements&quot;, intArray.Length);\n    Console.WriteLine();\n}\n<\/pre>\n<\/p>\n<p>Notice that when you make use of this \u201ccurly-bracket\u201d syntax, you do not need to specify the size of the array (seen when constructing the stringArray variable), given that this will be inferred by the number of items within the scope of the curly brackets. Also notice that the use of the new keyword is optional (shown when constructing the boolArray type).<br \/>\n\u8bf7\u6ce8\u610f\uff0c\u5f53\u60a8\u4f7f\u7528\u8fd9\u79cd\u201c\u82b1\u62ec\u53f7\u201d\u8bed\u6cd5\u65f6\uff0c\u60a8\u4e0d\u9700\u8981\u6307\u5b9a\u6570\u7ec4\u7684\u5927\u5c0f\uff08\u5728\u6784\u9020stringArray\u53d8\u91cf\u65f6\u53ef\u4ee5\u770b\u51fa\u6765\uff09\uff0c\u56e0\u4e3a\u8fd9\u5c06\u7531\u82b1\u62ec\u53f7\u8303\u56f4\u5185\u7684\u9879\u6570\u63a8\u65ad\u3002\u8fd8\u8981\u6ce8\u610f\uff0cnew\u5173\u952e\u5b57\u662f\u53ef\u9009\u7684\uff08\u5728\u6784\u9020boolArray\u7c7b\u578b\u65f6\u663e\u793a\uff09\u3002<\/p>\n<p>In the case of the intArray declaration, again recall the numeric value specified represents the number of elements in the array, not the value of the upper bound. If there is a mismatch between the declared size and the number of initializers (whether you have too many or too few initializers), you are issued a compile- time error. The following is an example:<br \/>\n\u5728intArray\u58f0\u660e\u7684\u60c5\u51b5\u4e0b\uff0c\u518d\u6b21\u8c03\u7528\u6307\u5b9a\u7684\u6570\u503c\u8868\u793a\u6570\u7ec4\u4e2d\u5143\u7d20\u7684\u6570\u91cf\uff0c\u800c\u4e0d\u662f\u4e0a\u754c\u7684\u503c\u3002\u5982\u679c\u58f0\u660e\u7684\u5927\u5c0f\u548c\u521d\u59cb\u5316\u7a0b\u5e8f\u7684\u6570\u91cf\u4e0d\u5339\u914d\uff08\u65e0\u8bba\u521d\u59cb\u5316\u7a0b\u5e8f\u592a\u591a\u8fd8\u662f\u592a\u5c11\uff09\uff0c\u90fd\u4f1a\u53d1\u51fa\u7f16\u8bd1\u65f6\u9519\u8bef\u3002\u4ee5\u4e0b\u662f\u4e00\u4e2a\u793a\u4f8b\uff1a<\/p>\n<p><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n\/\/ OOPS! Mismatch of size and elements!\n\/\/ \u54ce\u5440\uff01\u6570\u636e\u5927\u5c0f\u548c\u5143\u7d20\u6570\u91cf\u4e0d\u5339\u914d\uff01\nint&#x5B;] intArray = new int&#x5B;2] { 20, 22, 23, 0 };\n<\/pre>\n<\/p>\n<h2>Understanding Implicitly Typed Local Arrays<\/h2>\n<p>\u4e86\u89e3\u9690\u5f0f\u7c7b\u578b\u5316\u672c\u5730\u6570\u7ec4<\/p>\n<p>In Chapter 3, you learned about the topic of implicitly typed local variables. Recall that the var keyword allows you to define a variable, whose underlying type is determined by the compiler. In a similar vein, the var keyword can be used to define implicitly typed local arrays. Using this technique, you can allocate a new array variable without specifying the type contained within the array itself (note you must use the new keyword when using this approach).<br \/>\n\u5728\u7b2c3\u7ae0\uff0c\u5df2\u7ecf\u4e86\u89e3\u4e86\u9690\u5f0f\u7c7b\u578b\u5c40\u90e8\u53d8\u91cf\u7684\u4e3b\u9898\u3002\u56de\u60f3\u4e00\u4e0b\uff0cvar\u5173\u952e\u5b57\u5141\u8bb8\u5b9a\u4e49\u4e00\u4e2a\u53d8\u91cf\uff0c\u5176\u57fa\u672c\u7c7b\u578b\u7531\u7f16\u8bd1\u5668\u786e\u5b9a\u3002\u540c\u6837\uff0cvar\u5173\u952e\u5b57\u53ef\u4ee5\u5b9a\u4e49\u9690\u5f0f\u7c7b\u578b\u7684\u5c40\u90e8\u6570\u7ec4\u3002\u4f7f\u7528\u6b64\u6280\u672f\uff0c\u53ef\u4ee5\u5206\u914d\u4e00\u4e2a\u65b0\u7684\u6570\u7ec4\u53d8\u91cf\uff0c\u800c\u65e0\u9700\u6307\u5b9a\u6570\u7ec4\u672c\u8eab\u5305\u542b\u7684\u7c7b\u578b\uff08\u6ce8\u610f\uff0c\u4f7f\u7528\u6b64\u65b9\u6cd5\u65f6\u5fc5\u987b\u4f7f\u7528new\u5173\u952e\u5b57\uff09\u3002<\/p>\n<p><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nConsole.WriteLine(&quot;***** Fun with Arrays *****&quot;);\nDeclareImplicitArrays();\n\nstatic void DeclareImplicitArrays()\n{\n    Console.WriteLine(&quot;=&gt; Implicit Array Initialization.&quot;);\n    \/\/ a is really int&#x5B;].\n    \/\/ a \u5b9e\u9645\u4e0a\u662f\u6574\u6570\u6570\u7ec4\n    var a = new&#x5B;] { 1, 10, 100, 1000 };\n    Console.WriteLine(&quot;a is a: {0}&quot;, a.ToString());\n\n    \/\/ b is really double&#x5B;].\n    \/\/ b \u5b9e\u9645\u4e0a\u662fdouble\u6570\u7ec4\n    var b = new&#x5B;] { 1, 1.5, 2, 2.5 };\n    Console.WriteLine(&quot;b is a: {0}&quot;, b.ToString());\n\n    \/\/ c is really string&#x5B;].\n    \/\/ c \u5b9e\u9645\u4e0a\u662f\u5b57\u7b26\u4e32\u6570\u7ec4\n    var c = new&#x5B;] { &quot;hello&quot;, null, &quot;world&quot; };\n    Console.WriteLine(&quot;c is a: {0}&quot;, c.ToString());\n    Console.WriteLine();\n}\n<\/pre>\n<\/p>\n<p>Of course, just as when you allocate an array using explicit C# syntax, the items in the array\u2019s initialization list must be of the same underlying type (e.g., all ints, all strings, or all SportsCars). Unlike what you might be expecting, an implicitly typed local array does not default to System.Object; thus, the following generates a compile-time error:<br \/>\n\u5f53\u7136\uff0c\u5c31\u50cf\u4f7f\u7528\u663e\u5f0fC#\u8bed\u6cd5\u5206\u914d\u6570\u7ec4\u4e00\u6837\uff0c\u6570\u7ec4\u521d\u59cb\u5316\u5217\u8868\u4e2d\u7684\u9879\u5fc5\u987b\u5177\u6709\u76f8\u540c\u7684\u7c7b\u578b\uff08\u4f8b\u5982\uff0c\u5168\u90e8\u662f\u6574\u6570\u3001\u5168\u90e8\u662f\u5b57\u7b26\u4e32\u6216\u5168\u90e8\u662fSportsCars\u7c7b\u7684\u7c7b\u578b\uff09\u3002\u4e0e\u60a8\u53ef\u80fd\u671f\u671b\u7684\u4e0d\u540c\uff0c\u9690\u5f0f\u7c7b\u578b\u7684\u672c\u5730\u6570\u7ec4\u4e0d\u4f1a\u9ed8\u8ba4\u4e3aSystem.Object\u3002\u56e0\u6b64\uff0c\u4ee5\u4e0b\u751f\u6210\u7f16\u8bd1\u65f6\u9519\u8bef\uff1a<\/p>\n<p><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n\/\/ Error! Mixed types!\n\/\/ \u9519\u8bef\uff01\u6df7\u5408\u7c7b\u578b\uff01\nvar d = new&#x5B;] { 1, &quot;one&quot;, 2, &quot;two&quot;, false };\n<\/pre>\n<\/p>\n<h2>Defining an Array of Objects<\/h2>\n<p>\u5b9a\u4e49\u5bf9\u8c61\u6570\u7ec4<\/p>\n<p>In most cases, when you define an array, you do so by specifying the explicit type of item that can be within the array variable. While this seems quite straightforward, there is one notable twist. As you will come to understand in Chapter 6, System.Object is the ultimate base class to every type (including fundamental data types) in the .NET Core type system. Given this fact, if you were to define an array of System.Object data types, the subitems could be anything at all. Consider the following ArrayOfObjects() method:<br \/>\n\u5728\u5927\u591a\u6570\u60c5\u51b5\u4e0b\uff0c\u5b9a\u4e49\u6570\u7ec4\u65f6\uff0c\u53ef\u4ee5\u901a\u8fc7\u6307\u5b9a\u6570\u7ec4\u53d8\u91cf\u4e2d\u7684\u663e\u5f0f\u9879\u7c7b\u578b\u6765\u5b9e\u73b0\u3002\u867d\u7136\u8fd9\u770b\u8d77\u6765\u5f88\u7b80\u5355\uff0c\u4f46\u6709\u4e00\u4e2a\u503c\u5f97\u6ce8\u610f\u7684\u8f6c\u6298\u70b9\u3002\u60a8\u5c06\u5728\u7b2c6\u7ae0\u4e2d\u4e86\u89e3\u5230\uff0cSystem.Object\u662f.NET\u6240\u6709\u7c7b\u578b\uff08\u5305\u62ec\u57fa\u672c\u6570\u636e\u7c7b\u578b\uff09\u7684\u57fa\u7c7b\u3002\u8003\u8651\u5230\u8fd9\u4e2a\u4e8b\u5b9e\uff0c\u5982\u679c\u60a8\u8981\u5b9a\u4e49System.Object\u6570\u636e\u7c7b\u578b\u7684\u6570\u7ec4\uff0c\u90a3\u4e48\u5b50\u9879\u53ef\u4ee5\u662f\u4efb\u4f55\u4e1c\u897f\u3002\u8003\u8651\u4ee5\u4e0bArrayOfObjects() \u65b9\u6cd5\uff1a<\/p>\n<p><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nConsole.WriteLine(&quot;***** Fun with Arrays *****&quot;);\nArrayOfObjects();\n\nstatic void ArrayOfObjects()\n{\n    Console.WriteLine(&quot;=&gt; Array of Objects.&quot;);\n    \/\/ An array of objects can be anything at all.\n    \/\/ \u5bf9\u8c61\u6570\u7ec4\u53ef\u4ee5\u662f\u4efb\u4f55\u4e1c\u897f\u3002\n    \/\/ \u8fd9\u91cc\u662f\u5b9a\u4e49\u4e864\u4e2a\u5bf9\u8c61\u3002\u6bcf\u4e2a\u5bf9\u8c61\u7684\u6570\u503c\u7c7b\u578b\u90fd\u4e0d\u4e00\u6837\u3002\n    object&#x5B;] myObjects = new object&#x5B;4];\n    myObjects&#x5B;0] = 10;\n    myObjects&#x5B;1] = false;\n    myObjects&#x5B;2] = new DateTime(1969, 3, 24);\n    myObjects&#x5B;3] = &quot;Form &amp; Void&quot;;\n    foreach (object obj in myObjects)\n    {\n        \/\/ Print the type and value for each item in array.\n        \/\/ \u6253\u5370\u6570\u7ec4\u4e2d\u6bcf\u4e2a\u9879\u76ee\u7684\u7c7b\u578b\u548c\u503c\u3002\n        Console.WriteLine(&quot;Type: {0}, Value: {1}&quot;, obj.GetType(), obj);\n    }\n    Console.WriteLine();\n}\n<\/pre>\n<\/p>\n<p>Here, as you are iterating over the contents of myObjects, you print the underlying type of each item using the GetType() method of System.Object, as well as the value of the current item. Without going into too much detail regarding System.Object.GetType() at this point in the text, simply understand that this method can be used to obtain the fully qualified name of the item (Chapter 17 examines the topic of type information and reflection services in detail). The following output shows the result of calling ArrayOfObjects():<br \/>\n\u5728\u8fd9\u91cc\uff0c\u8fed\u4ee3myObjects\u7684\u5185\u5bb9\u65f6\uff0c\u53ef\u4ee5\u4f7f\u7528System.Object\u7684GetType()\u65b9\u6cd5\u6253\u5370\u6bcf\u4e2a\u9879\u7684\u5e95\u5c42\u7c7b\u578b\uff0c\u4ee5\u53ca\u5f53\u524d\u9879\u7684\u503c\u3002\u73b0\u5728\uff0c\u4e0d\u9700\u8981\u592a\u591a\u5173\u6ce8System.Object.GetType()\u7684\u7ec6\u8282\uff0c\u53ea\u9700\u7406\u89e3\u8fd9\u79cd\u65b9\u6cd5\u53ef\u4ee5\u7528\u6765\u83b7\u5f97\u9879\u76ee\u7684\u5b8c\u5168\u9650\u5b9a\u540d\u79f0\uff08\u7b2c17\u7ae0\u8be6\u7ec6\u4ecb\u7ecd\u4e86\u7c7b\u578b\u4fe1\u606f\u548c\u53cd\u5c04\u7684\u4e3b\u9898\uff09\u3002\u4ee5\u4e0b\u8f93\u51fa\u662f\u8c03\u7528ArrayOfObjects()\u7684\u7ed3\u679c\uff1a<\/p>\n<p><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n***** Fun with Arrays *****\n=&gt; Array of Objects.\nType: System.Int32, Value: 10\nType: System.Boolean, Value: False\nType: System.DateTime, Value: 1969\/3\/24 0:00:00\nType: System.String, Value: Form &amp; Void\n<\/pre>\n<\/p>\n<h2>Working with Multidimensional Arrays<\/h2>\n<p>\u4f7f\u7528\u591a\u7ef4\u6570\u7ec4<\/p>\n<p>In addition to the single dimension arrays you have seen thus far, C# supports two varieties of multidimensional arrays. The first of these is termed a rectangular array, which is simply an array of multiple dimensions, where each row is of the same length. To declare and fill a multidimensional rectangular array, proceed as follows:<br \/>\n\u9664\u4e86\u60a8\u8fc4\u4eca\u4e3a\u6b62\u770b\u5230\u7684\u4e00\u7ef4\u6570\u7ec4\u4e4b\u5916\uff0cC#\u8fd8\u652f\u6301\u4e24\u79cd\u7c7b\u578b\u7684\u591a\u7ef4\u6570\u7ec4\u3002\u7b2c\u4e00\u79cd\u7c7b\u578b\u88ab\u79f0\u4e3a\u77e9\u5f62\u9635\u5217\uff08\u77e9\u5f62\u6570\u7ec4\uff09\uff0c\u5b83\u53ea\u662f\u4e00\u4e2a\u591a\u4e2a\u7ef4\u5ea6\u7684\u9635\u5217\uff0c\u5176\u4e2d\u6bcf\u884c\u7684\u957f\u5ea6\u76f8\u540c\u3002\u8981\u58f0\u660e\u548c\u586b\u5145\u591a\u7ef4\u77e9\u5f62\u6570\u7ec4\uff0c\u8bf7\u6267\u884c\u4ee5\u4e0b\u64cd\u4f5c\uff1a<\/p>\n<p><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nConsole.WriteLine(&quot;***** Fun with Arrays *****&quot;);\nRectMultidimensionalArray();\n\nstatic void RectMultidimensionalArray()\n{\n    Console.WriteLine(&quot;=&gt; Rectangular multidimensional array.&quot;);\n    \/\/ A rectangular multidimensional array.\n    \/\/ \u77e9\u5f62\u591a\u7ef4\u6570\u7ec4\u3002\n    int&#x5B;,] myMatrix;\n    myMatrix = new int&#x5B;3, 4];\n    \/\/ Populate (3 * 4) array.\n    \/\/ \u586b\u5145 \uff083 * 4\uff09 \u6570\u7ec4\u9635\u5217\n    for (int i = 0; i &lt; 3; i++)\n    {\n        for (int j = 0; j &lt; 4; j++)\n        {\n            myMatrix&#x5B;i, j] = i * j;\n        }\n    }\n\n    \/\/ Print (3 * 4) array.\n    \/\/ \u6253\u5370\n    for (int i = 0; i &lt; 3; i++)\n    {\n        for (int j = 0; j &lt; 4; j++)\n        {\n            Console.Write(myMatrix&#x5B;i, j] + &quot;\\t&quot;);\n        }\n        Console.WriteLine();\n    }\n    Console.WriteLine();\n}\n<\/pre>\n<\/p>\n<p>The second type of multidimensional array is termed a jagged array. As the name implies, jagged arrays contain some number of inner arrays, each of which may have a different upper limit. Here is an example:<br \/>\n\u7b2c\u4e8c\u79cd\u7c7b\u578b\u7684\u591a\u7ef4\u6570\u7ec4\u79f0\u4e3a\u952f\u9f7f\u72b6\u6570\u7ec4\u3002\u987e\u540d\u601d\u4e49\uff0c\u952f\u9f7f\u72b6\u6570\u7ec4\u5305\u542b\u4e00\u5b9a\u6570\u91cf\u7684\u5185\u90e8\u6570\u7ec4\uff0c\u6bcf\u4e2a\u5185\u90e8\u6570\u7ec4\u53ef\u80fd\u6709\u4e0d\u540c\u7684\u4e0a\u9650\u3002\u4ee5\u4e0b\u662f\u4e00\u4e2a\u793a\u4f8b\uff1a<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nConsole.WriteLine(&quot;***** Fun with Arrays *****&quot;);\nJaggedMultidimensionalArray();\n\nstatic void JaggedMultidimensionalArray()\n{\n    Console.WriteLine(&quot;=&gt; Jagged multidimensional array.&quot;);\n    \/\/ A jagged MD array (i.e., an array of arrays).\n    \/\/ \u4ea4\u9519\u7684\u591a\u7ef4\u6570\u7ec4\uff08\u5373\u6570\u7ec4\u7684\u6570\u7ec4\uff09\u3002\n    \/\/ Here we have an array of 5 different arrays.\n    \/\/ \u91cc\u6211\u4eec\u6709\u4e00\u4e2a\u5305\u542b 5 \u4e2a\u4e0d\u540c\u6570\u7ec4\u7684\u6570\u7ec4\u3002\n    int&#x5B;]&#x5B;] myJagArray = new int&#x5B;5]&#x5B;];\n    \/\/ Create the jagged array.\n    \/\/ \u521b\u5efa\u4ea4\u9519\u6570\u7ec4\u3002\n    for (int i = 0; i &lt; myJagArray.Length; i++)\n    {\n        myJagArray&#x5B;i] = new int&#x5B;i + 7];\n    }\n\n    \/\/ Print each row (remember, each element is defaulted to zero!).\n    \/\/ \u6253\u5370\u6bcf\u4e00\u884c\uff08\u8bf7\u8bb0\u4f4f\uff0c\u6bcf\u4e2a\u5143\u7d20\u9ed8\u8ba4\u4e3a\u96f6\uff01\uff09\n    for (int i = 0; i &lt; 5; i++)\n    {\n        for (int j = 0; j &lt; myJagArray&#x5B;i].Length; j++)\n        {\n            Console.Write(myJagArray&#x5B;i]&#x5B;j] + &quot; &quot;);\n        }\n        Console.WriteLine();\n    }\n    Console.WriteLine();\n}\n<\/pre>\n<\/p>\n<p>The output of calling each of the RectMultidimensionalArray() and JaggedMultidimensionalArray() methods is shown next:<br \/>\n\u8c03\u7528RectMultidimensionalArray()\u548cJaggedMultidimensionalArray()\u4e24\u4e2a\u65b9\u6cd5\u7684\u8f93\u51fa\u5982\u4e0b\uff1a<\/p>\n<p><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n***** Fun with Arrays *****\n=&gt; Rectangular multidimensional array.\n0       0       0       0\n0       1       2       3\n0       2       4       6\n\n=&gt; Jagged multidimensional array.\n0 0 0 0 0 0 0\n0 0 0 0 0 0 0 0\n0 0 0 0 0 0 0 0 0\n0 0 0 0 0 0 0 0 0 0\n0 0 0 0 0 0 0 0 0 0 0\n<\/pre>\n<\/p>\n<h2>Using Arrays As Arguments or Return Values<\/h2>\n<p>\u4f7f\u7528\u6570\u7ec4\u4f5c\u4e3a\u53c2\u6570\u6216\u8fd4\u56de\u503c<\/p>\n<p>After you have created an array, you are free to pass it as an argument or receive it as a member return value. For example, the following PrintArray() method takes an incoming array of ints and prints each member to the console, while the GetStringArray() method populates an array of strings and returns it to the caller:<\/p>\n<p>After you have created an array, you are free to pass it as an argument or receive it as a member return value. For example, the following PrintArray() method takes an incoming array of ints and prints each member to the console, while the GetStringArray() method populates an array of strings and returns it to the caller:<br \/>\n\u521b\u5efa\u6570\u7ec4\u540e\uff0c\u53ef\u4ee5\u5c06\u5176\u4f5c\u4e3a\u53c2\u6570\u4f20\u9012\u6216\u4f5c\u4e3a\u65b9\u6cd5\u8fd4\u56de\u503c\u3002\u4f8b\u5982\uff0c\u4ee5\u4e0bPrintArray()\u65b9\u6cd5\u83b7\u53d6\u4e00\u4e2a\u4f20\u5165\u7684int\u6570\u7ec4\u5e76\u5c06\u6bcf\u4e2a\u6210\u5458\u6253\u5370\u5230\u63a7\u5236\u53f0\uff0c\u800cGetStringArray()\u65b9\u6cd5\u586b\u5145\u4e00\u4e2a\u5b57\u7b26\u4e32\u6570\u7ec4\u5e76\u5c06\u5176\u8fd4\u56de\u7ed9\u8c03\u7528\u65b9\uff1a<\/p>\n<p><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nstatic void PrintArray(int&#x5B;] myInts)\n{\n    for (int i = 0; i &lt; myInts.Length; i++)\n    {\n        Console.WriteLine(&quot;Item {0} is {1}&quot;, i, myInts&#x5B;i]);\n    }\n}\n\nstatic string&#x5B;] GetStringArray()\n{\n    string&#x5B;] theStrings = { &quot;Hello&quot;, &quot;from&quot;, &quot;GetStringArray&quot; };\n    return theStrings;\n}\n<\/pre>\n<\/p>\n<p>These methods can be invoked as you would expect.<br \/>\n\u53ef\u4ee5\u6309\u9884\u671f\u8c03\u7528\u8fd9\u4e9b\u65b9\u6cd5\u3002<\/p>\n<p><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nConsole.WriteLine(&quot;***** Fun with Arrays *****&quot;);\nPassAndReceiveArrays();\n\nstatic void PassAndReceiveArrays()\n{\n    Console.WriteLine(&quot;=&gt; Arrays as params and return values.&quot;);\n    \/\/ Pass array as parameter.\n    \/\/ \u5c06\u6570\u7ec4\u4f5c\u4e3a\u53c2\u6570\u4f20\u9012\u3002\n    int&#x5B;] ages = { 20, 22, 23, 0 };\n    PrintArray(ages);\n    \/\/ Get array as return value.\n    \/\/ \u83b7\u53d6\u6570\u7ec4\u4f5c\u4e3a\u8fd4\u56de\u503c\u3002\n    string&#x5B;] strs = GetStringArray();\n    foreach (string s in strs)\n    {\n        Console.WriteLine(s);\n    }\n    Console.WriteLine();\n}\n\n\/\/ \u63a5\u53d7\u6570\u7ec4\u53c2\u6570\uff0c\u904d\u5386\u6570\u636e\u5e76\u5728\u63a7\u5236\u53f0\u6253\u5370\nstatic void PrintArray(int&#x5B;] myInts)\n{\n    for (int i = 0; i &lt; myInts.Length; i++)\n    {\n        Console.WriteLine(&quot;Item {0} is {1}&quot;, i, myInts&#x5B;i]);\n    }\n}\n\n\/\/ \u8fd4\u56de\u503c\u662f\u6570\u7ec4\nstatic string&#x5B;] GetStringArray()\n{\n    string&#x5B;] theStrings = { &quot;Hello&quot;, &quot;from&quot;, &quot;GetStringArray&quot; };\n    return theStrings;\n}\n<\/pre>\n<\/p>\n<p>At this point, you should feel comfortable with the process of defining, filling, and examining the contents of a C# array variable. To complete the picture, let\u2019s now examine the role of the System.Array class.<br \/>\n\u6b64\u65f6\uff0c\u60a8\u5e94\u8be5\u5bf9\u5b9a\u4e49\u3001\u586b\u5145\u6570\u7ec4\u53d8\u91cf\u7684\u8fc7\u7a0b\u611f\u5230\u6ee1\u610f\u3002\u4e3a\u4e86\u5b8c\u6210\u8fd9\u5e45\u62fc\u56fe\uff0c\u73b0\u5728\u8ba9\u6211\u4eec\u63a2\u8ba8\u4e00\u4e0bSystem.Array\u7c7b\u7684\u4f5c\u7528\u3002<\/p>\n<h2>Using the System.Array Base Class<\/h2>\n<p>\u4f7f\u7528 System.Array \u57fa\u7c7b<\/p>\n<p>Every array you create gathers much of its functionality from the System.Array class. Using these common members, you can operate on an array using a consistent object model. Table 4-1 gives a rundown of some of the more interesting members (be sure to check the documentation for full details).<br \/>\n\u60a8\u521b\u5efa\u7684\u6bcf\u4e2a\u6570\u7ec4\u90fd\u4eceSystem.array\u7c7b\u4e2d\u83b7\u5f97\u5176\u5927\u90e8\u5206\u529f\u80fd\u3002\u4f7f\u7528\u8fd9\u4e9b\u901a\u7528\u6210\u5458\uff08\u65b9\u6cd5\u6216\u5c5e\u6027\uff09\uff0c\u53ef\u4ee5\u4f7f\u7528\u4e00\u81f4\u7684\u5bf9\u8c61\u6a21\u578b\u5bf9\u6570\u7ec4\u8fdb\u884c\u64cd\u4f5c\u3002\u88684-1\u5217\u51fa\u4e86\u4e00\u4e9b\u66f4\u6709\u8da3\u7684\u6210\u5458\uff08\u8bf7\u52a1\u5fc5\u67e5\u770b\u6587\u6863\u4ee5\u83b7\u53d6\u5b8c\u6574\u8be6\u7ec6\u4fe1\u606f\uff09\u3002<\/p>\n<p>Table 4-1. Select Members of System.Array<br \/>\n\u88684-1. System.Array\u7684\u6210\u5458<\/p>\n<table>\n<tr>\n<td>Member\u00a0of Array\u00a0Class<\/td>\n<td>Meaning\u00a0in\u00a0Life<\/td>\n<\/tr>\n<tr>\n<td>Clear()<\/td>\n<td>This\u00a0static\u00a0method\u00a0sets\u00a0a\u00a0range\u00a0of\u00a0elements\u00a0in\u00a0the\u00a0array\u00a0to\u00a0empty\u00a0values\u00a0(0\u00a0for\u00a0numbers,null\u00a0for\u00a0object\u00a0references,\u00a0false\u00a0for\u00a0Booleans).<br \/>\n        \u6b64\u9759\u6001\u65b9\u6cd5\u5c06\u6570\u7ec4\u4e2d\u7684\u5143\u7d20\u8303\u56f4\u8bbe\u7f6e\u4e3a\u7a7a\u503c\uff08\u6570\u5b57\u8bbe\u7f6e\u4e3a0\uff0c\u5bf9\u8c61\u8bbe\u7f6e\u4e3anull\uff0c\u5e03\u5c14\u503c\u8bbe\u7f6e\u4e3afalse\uff09\u3002<\/td>\n<\/tr>\n<tr>\n<td>CopyTo()<\/td>\n<td>This\u00a0method\u00a0is\u00a0used\u00a0to\u00a0copy\u00a0elements\u00a0from\u00a0the\u00a0source\u00a0array\u00a0into\u00a0the\u00a0destination\u00a0array.<br \/>\n        \u6b64\u65b9\u6cd5\u7528\u4e8e\u5c06\u5143\u7d20\u4ece\u6e90\u6570\u7ec4\u590d\u5236\u5230\u76ee\u6807\u6570\u7ec4\u4e2d\u3002<\/td>\n<\/tr>\n<tr>\n<td>Length<\/td>\n<td>This\u00a0property\u00a0returns\u00a0the\u00a0number\u00a0of\u00a0items\u00a0within\u00a0the\u00a0array.<br \/>\n        \u6b64\u5c5e\u6027\u8fd4\u56de\u6570\u7ec4\u4e2d\u7684\u9879\u6570 \u3002<\/td>\n<\/tr>\n<tr>\n<td>Rank<\/td>\n<td>This\u00a0property\u00a0returns\u00a0the\u00a0number\u00a0of\u00a0dimensions\u00a0of\u00a0the\u00a0current\u00a0array.<br \/>\n        \u6b64\u5c5e\u6027\u8fd4\u56de\u5f53\u524d\u6570\u7ec4\u7684\u7ef4\u6570 \u3002\n        <\/td>\n<\/tr>\n<tr>\n<td>Reverse()<\/td>\n<td>This\u00a0static\u00a0method\u00a0reverses\u00a0the\u00a0contents\u00a0of\u00a0a\u00a0one-dimensional\u00a0array.<br \/>\n        \u6b64\u9759\u6001\u65b9\u6cd5\u53cd\u8f6c\u4e00\u7ef4\u6570\u7ec4\u7684\u5185\u5bb9 \u3002<\/td>\n<\/tr>\n<tr>\n<td>Sort()<\/td>\n<td>This\u00a0static\u00a0method\u00a0sorts\u00a0a\u00a0one-dimensional\u00a0array\u00a0of\u00a0intrinsic\u00a0types.\u00a0If\u00a0the\u00a0elements\u00a0in the\u00a0array\u00a0implement\u00a0the\u00a0IComparer\u00a0interface,\u00a0you\u00a0can\u00a0also\u00a0sort\u00a0your\u00a0custom\u00a0types\u00a0(see\u00a0Chapters\u00a08\u00a0and\u00a010).<br \/>\n        \u6b64\u9759\u6001\u65b9\u6cd5\u5bf9\u5185\u90e8\u7c7b\u578b\u7684\u4e00\u7ef4\u6570\u7ec4\u8fdb\u884c\u6392\u5e8f\u3002\u5982\u679c\u6570\u7ec4\u4e2d\u7684\u5143\u7d20\u5b9e\u73b0IComparer\u63a5\u53e3\uff0c\u60a8\u4e5f\u53ef\u4ee5\u5bf9\u81ea\u5b9a\u4e49\u7c7b\u578b\u8fdb\u884c\u6392\u5e8f\uff08\u8bf7\u53c2\u9605\u7b2c8\u7ae0\u548c\u7b2c10\u7ae0\uff09\u3002<\/td>\n<\/tr>\n<\/table>\n<p>Let\u2019s see some of these members in action. The following helper method makes use of the static Reverse() and Clear() methods to pump out information about an array of string types to the console:<br \/>\n\u8ba9\u6211\u4eec\u770b\u770b\u5176\u4e2d\u4e00\u4e9b\u6210\u5458\u7684\u64cd\u4f5c\u3002\u4ee5\u4e0bhelper\u65b9\u6cd5\u4f7f\u7528\u9759\u6001Reverse()\u548cClear()\u65b9\u6cd5\u5c06\u5b57\u7b26\u4e32\u7c7b\u578b\u6570\u7ec4\u7684\u4fe1\u606f\u8f93\u51fa\u5230\u63a7\u5236\u53f0\uff1a<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nConsole.WriteLine(&quot;***** Fun with Arrays *****&quot;);\nSystemArrayFunctionality();\n\nstatic void SystemArrayFunctionality()\n{\n    Console.WriteLine(&quot;=&gt; Working with System.Array.&quot;);\n    \/\/ Initialize items at startup.\n    \/\/ \u5728\u542f\u52a8\u65f6\u521d\u59cb\u5316\u6570\u7ec4\u6bcf\u4e00\u9879\u3002\n    string&#x5B;] gothicBands = { &quot;Tones on Tail&quot;, &quot;Bauhaus&quot;, &quot;Sisters of Mercy&quot; };\n\n    \/\/ Print out names in declared order.\n    \/\/ \u6309\u58f0\u660e\uff08\u5b9a\u4e49\u7684\uff09\u987a\u5e8f\u6253\u5370\u59d3\u540d\u3002\n    Console.WriteLine(&quot;-&gt; Here is the array:&quot;);\n    for (int i = 0; i &lt; gothicBands.Length; i++)\n    {\n        \/\/ Print a name.\n        Console.Write(gothicBands&#x5B;i] + &quot;, &quot;);\n    }\n    Console.WriteLine(&quot;\\n&quot;);\n\n    \/\/ Reverse them...\n    \/\/ \u53cd\u8f6c\u4ed6\u4eec\n    Array.Reverse(gothicBands);\n    Console.WriteLine(&quot;-&gt; The reversed array&quot;);\n\n    \/\/ ... and print them.\n    \/\/ \u5e76\u6253\u5370\u5b83\u4eec\n    for (int i = 0; i &lt; gothicBands.Length; i++)\n    {\n        \/\/ Print a name.\n        \/\/ \u6253\u5370\u59d3\u540d\n        Console.Write(gothicBands&#x5B;i] + &quot;, &quot;);\n    }\n    Console.WriteLine(&quot;\\n&quot;);\n\n    \/\/ Clear out all but the first member.\n    \/\/ \u6e05\u9664\u7b2c\u4e00\u4e2a\u6210\u5458\u4e4b\u5916\u7684\u6240\u6709\u6210\u5458\u3002\n    Console.WriteLine(&quot;-&gt; Cleared out all but one...&quot;);\n    Array.Clear(gothicBands, 1, 2);\n    for (int i = 0; i &lt; gothicBands.Length; i++)\n    {\n        \/\/ Print a name.\n        \/\/ \u6253\u5370\u59d3\u540d\n        Console.Write(gothicBands&#x5B;i] + &quot;, &quot;);\n    }\n    Console.WriteLine();\n}\n<\/pre>\n<\/p>\n<p>If you invoke this method, you will get the output shown here:<br \/>\n\u5982\u679c\u8c03\u7528\u6b64\u65b9\u6cd5\uff0c \u5c06\u83b7\u5f97\u5982\u4e0b\u6240\u793a\u7684\u8f93\u51fa \uff1a<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n***** Fun with Arrays *****\n=&gt; Working with System.Array.\n-&gt; Here is the array:\nTones on Tail, Bauhaus, Sisters of Mercy,\n\n-&gt; The reversed array\nSisters of Mercy, Bauhaus, Tones on Tail,\n\n-&gt; Cleared out all but one...\nSisters of Mercy, , ,\n<\/pre>\n<\/p>\n<p>Notice that many members of System.Array are defined as static members and are, therefore, called at the class level (e.g., the Array.Sort() and Array.Reverse() methods). Methods such as these are passed in the array you want to process. Other members of System.Array (such as the Length property) are bound at the object level; thus, you can invoke the member directly on the array.<br \/>\n\u8bf7\u6ce8\u610f\uff0cSystem.Array\u7684\u8bb8\u591a\u6210\u5458\u90fd\u88ab\u5b9a\u4e49\u4e3a\u9759\u6001\u6210\u5458\uff08\u4f8b\u5982\uff0cArray.Sort()\u548cArray.Reverse()\u65b9\u6cd5\uff09\uff0c\u56e0\u6b64\u53ef\u4ee5\u5728\u7c7b\u7ea7\u522b\u88ab\u8c03\u7528\uff0c\u4f20\u9012\u8981\u5904\u7406\u7684\u6570\u7ec4\u3002System.Array\u7684\u5176\u4ed6\u6210\u5458\uff08\u5982Length\u5c5e\u6027\uff09\u5728\u5bf9\u8c61\u7ea7\u522b\u7ed1\u5b9a\uff1b\u56e0\u6b64\uff0c\u53ef\u4ee5\u76f4\u63a5\u5728\u6570\u7ec4\u4e0a\u8c03\u7528\u8be5\u6210\u5458\u3002<\/p>\n<h2>Using Indices and Ranges (New 8.0, Updated 10.0)<\/h2>\n<p>\u4f7f\u7528\u7d22\u5f15\u548c\u8303\u56f4\uff08\u65b0\u7248 8.0\uff0c\u66f4\u65b0\u7684 10.0\uff09<\/p>\n<p>To simplify working with sequences (including arrays), C# 8 introduces two new types and two new operators for use when working with arrays.<br \/>\n\u4e3a\u4e86\u7b80\u5316\u5bf9\u5e8f\u5217\uff08\u5305\u62ec\u6570\u7ec4\uff09\u7684\u5904\u7406\uff0cC#8\u5f15\u5165\u4e86\u4e24\u79cd\u65b0\u7c7b\u578b\u548c\u4e24\u79cd\u65b0\u8fd0\u7b97\u7b26\uff0c\u4ee5\u4fbf\u5728\u5904\u7406\u6570\u7ec4\u65f6\u4f7f\u7528\u3002<\/p>\n<ul>\n<li>System.Index represents an index into a sequence.<br \/>\nSystem.Index \u8868\u793a\u5e8f\u5217\u4e2d\u7684\u7d22\u5f15\u3002<\/li>\n<li>System.Range represents a subrange of indices.<br \/>\nSystem.Range \u8868\u793a\u7d22\u5f15\u7684\u5b50\u8303\u56f4\u3002<\/li>\n<li>The index from end operator (^) specifies that the index is relative to the end of the sequence.<br \/>\n\u7ed3\u675f\u8fd0\u7b97\u7b26\uff08^\uff09\u6307\u5b9a\u7d22\u5f15\u76f8\u5bf9\u4e8e\u5e8f\u5217\u7684\u7ed3\u675f\u3002<\/li>\n<li>The range operator (..) specifies the start and end of a range as its operands.<br \/>\n\u8303\u56f4\u8fd0\u7b97\u7b26\uff08..\uff09\u6307\u5b9a\u8303\u56f4\u7684\u5f00\u59cb\u548c\u7ed3\u675f\u4f5c\u4e3a\u5176\u64cd\u4f5c\u6570\u3002<\/li>\n<\/ul>\n<blockquote><p>\n\u25a0 Note  indices and ranges can be used with arrays, strings, Span<T>, ReadOnlySpan<T>, and (added in .net 6\/C# 10) IEnumerable<T>.<br \/>\n\u6ce8\u610f \u7d22\u5f15\u548c\u8303\u56f4\u53ef\u4ee5\u4e0earrays, strings, Span<T>, ReadOnlySpan<T>, IEnumerable<T>\u4e00\u8d77\u4f7f\u7528\u3002\n<\/p><\/blockquote>\n<p>As you have already seen, arrays are indexed beginning with zero (0). The end of a sequence is the length of the sequence \u2013 1. The previous for loop that printed the gothicBands array can be updated to the following:<br \/>\n\u6b63\u5982\u60a8\u5df2\u7ecf\u770b\u5230\u7684\uff0c\u6570\u7ec4\u7684\u7d22\u5f15\u4ece\u96f6\uff080\uff09\u5f00\u59cb\u3002\u5e8f\u5217\u7684\u672b\u5c3e\u662f\u5e8f\u5217\u7684\u957f\u5ea6-1\u3002\u4e4b\u524d\u6253\u5370gothicBands\u6570\u7ec4\u7684for\u5faa\u73af\u53ef\u4ee5\u66f4\u65b0\u4e3a\u4ee5\u4e0b\u5185\u5bb9\uff1a<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nConsole.WriteLine(&quot;***** Fun with Arrays *****&quot;);\n\nstring&#x5B;] gothicBands = { &quot;Tones on Tail&quot;, &quot;Bauhaus&quot;, &quot;Sisters of Mercy&quot; };\n\nfor (int i = 0; i &lt; gothicBands.Length; i++)\n{\n    Index idx = i;\n    \/\/ Print a name\n    Console.Write(gothicBands&#x5B;idx] + &quot;, &quot;);\n}\n<\/pre>\n<\/p>\n<p>The index from end operator lets you specify how many positions from the end of sequence, starting with the length. Remember that the last item in a sequence is one less than the actual length, so ^0 would cause an error. The following code prints the array in reverse:<br \/>\n\u7d22\u5f15\u8d77\u59cb\u70b9\u8fd0\u7b97\u7b26\u7528\u4e8e\u6307\u5b9a\u4ece\u5e8f\u5217\u672b\u5c3e\u5f00\u59cb\u7684\u4f4d\u7f6e\u6570\uff0c\u4ece\u957f\u5ea6\u5f00\u59cb\u3002\u8bf7\u8bb0\u4f4f\uff0c\u5e8f\u5217\u4e2d\u7684\u6700\u540e\u4e00\u9879\u6bd4\u5b9e\u9645\u957f\u5ea6\u5c11\u4e00\u9879\uff0c\u56e0\u6b64^0\u4f1a\u5bfc\u81f4\u9519\u8bef\u3002\u4ee5\u4e0b\u4ee3\u7801\u53cd\u5411\u6253\u5370\u6570\u7ec4\uff1a<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nConsole.WriteLine(&quot;***** Fun with Arrays *****&quot;);\n\nstring&#x5B;] gothicBands = { &quot;Tones on Tail&quot;, &quot;Bauhaus&quot;, &quot;Sisters of Mercy&quot; };\n\nfor (int i = 1; i &lt;= gothicBands.Length; i++)\n{\n    Index idx = ^i;\n    \/\/ Print a name\n    Console.Write(gothicBands&#x5B;idx] + &quot;, &quot;);\n}\n<\/pre>\n<\/p>\n<p>The range operator specifies a start and end index and allows for access to a subsequence within a list. The start of the range is inclusive, and the end of the range is exclusive. For example, to pull out the first two members of the array, create ranges from 0 (the first member) to 2 (one more than the desired index position).<br \/>\n\u8303\u56f4\u8fd0\u7b97\u7b26\u6307\u5b9a\u5f00\u59cb\u548c\u7ed3\u675f\u7d22\u5f15\uff0c\u5e76\u5141\u8bb8\u8bbf\u95ee\u5217\u8868\u4e2d\u7684\u5b50\u5e8f\u5217\u3002\u8303\u56f4\u7684\u5f00\u59cb\u662f\u5305\u542b\u7684\uff0c\u8303\u56f4\u7684\u7ed3\u675f\u662f\u6392\u9664\u7684\u3002\u4f8b\u5982\uff0c\u8981\u62c9\u51fa\u6570\u7ec4\u7684\u524d\u4e24\u4e2a\u6210\u5458\uff0c\u8bf7\u521b\u5efa\u4ece0\uff08\u7b2c\u4e00\u4e2a\u6210\u5458\uff09\u52302\uff08\u6bd4\u6240\u9700\u7d22\u5f15\u4f4d\u7f6e\u591a\u4e00\u4e2a\uff09\u7684\u8303\u56f4\u3002<\/p>\n<p><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nConsole.WriteLine(&quot;***** Fun with Arrays *****&quot;);\n\nstring&#x5B;] gothicBands = { &quot;Tones on Tail&quot;, &quot;Bauhaus&quot;, &quot;Sisters of Mercy&quot; };\n\nforeach (var itm in gothicBands&#x5B;0..2])\n{\n    \/\/ Print a name\n    Console.Write(itm + &quot;, &quot;);\n}\nConsole.WriteLine(&quot;\\n&quot;);\n<\/pre>\n<\/p>\n<p>Ranges can also be passed to a sequence using the new Range data type, as shown here:<br \/>\n\u4e5f\u53ef\u4ee5\u4f7f\u7528\u65b0\u7684\u8303\u56f4\u6570\u636e\u7c7b\u578b\u5c06\u8303\u56f4\u4f20\u9012\u7ed9\u5e8f\u5217\uff0c\u5982\u4e0b\u6240\u793a\uff1a<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nConsole.WriteLine(&quot;***** Fun with Arrays *****&quot;);\n\nstring&#x5B;] gothicBands = { &quot;Tones on Tail&quot;, &quot;Bauhaus&quot;, &quot;Sisters of Mercy&quot; };\n\nRange r = 0..2;\n\/\/  the end of the range is exclusive\n\/\/ \u8303\u56f4\u7684\u672b\u5c3e\u662f\u6392\u9664\u7684\nforeach (var itm in gothicBands&#x5B;r])\n{\n    \/\/ Print a name\n    Console.Write(itm + &quot;, &quot;);\n}\nConsole.WriteLine(&quot;\\n&quot;);\n<\/pre>\n<\/p>\n<p>Ranges can be defined using integers or Index variables. The same result will occur with the following code:<br \/>\n\u53ef\u4ee5\u4f7f\u7528\u6574\u6570\u6216\u7d22\u5f15\u53d8\u91cf\u5b9a\u4e49\u8303\u56f4\u3002\u4ee5\u4e0b\u4ee3\u7801\u5c06\u51fa\u73b0\u76f8\u540c\u7684\u7ed3\u679c\uff1a<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nConsole.WriteLine(&quot;***** Fun with Arrays *****&quot;);\n\nstring&#x5B;] gothicBands = { &quot;Tones on Tail&quot;, &quot;Bauhaus&quot;, &quot;Sisters of Mercy&quot; };\n\nIndex idx1 = 0;\nIndex idx2 = 2;\nRange r = idx1..idx2; \n\/\/the end of the range is exclusive\nforeach (var itm in gothicBands&#x5B;r])\n{\n    \/\/ Print a name\n    Console.Write(itm + &quot;, &quot;);\n}\nConsole.WriteLine(&quot;\\n&quot;);\n<\/pre>\n<\/p>\n<p>If the beginning of the range is left off, the beginning of the sequence is used. If the end of the range is left off, the length of the range is used. This does not cause an error, since the value at the end of the range is exclusive. For the previous example of three items in an array, all the ranges represent the same subset.<br \/>\n\u5982\u679c\u4e0d\u6307\u5b9a\u8303\u56f4\u7684\u5f00\u5934\uff0c\u5219\u4f7f\u7528\u5e8f\u5217\u7684\u5f00\u5934\u3002\u5982\u679c\u4e0d\u6307\u5b9a\u8303\u56f4\u7684\u672b\u5c3e\uff0c\u5219\u4f7f\u7528\u8303\u56f4\u7684\u957f\u5ea6\u3002\u8fd9\u4e0d\u4f1a\u5bfc\u81f4\u9519\u8bef\uff0c\u56e0\u4e3a\u8303\u56f4\u672b\u5c3e\u7684\u503c\u662f\u72ec\u5360\u7684\u3002\u5bf9\u4e8e\u524d\u9762\u7684\u6570\u7ec4\u4e2d\u4e09\u4e2a\u9879\u76ee\u7684\u793a\u4f8b\uff0c\u6240\u6709\u8303\u56f4\u90fd\u8868\u793a\u76f8\u540c\u7684\u5b50\u96c6\u3002<\/p>\n<p><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\ngothicBands&#x5B;..]\ngothicBands&#x5B;0..^0]\ngothicBands&#x5B;0..3]\n<\/pre>\n<\/p>\n<p>The ElementAt() extension method (in the System.Linq namespace) retrieves the element from the array at the specified location. New in .NET 6\/C# 10, using the index from end operator is supported to get an element the specified distance from the end of the array. The following code gets the second-to-last band from the list:<br \/>\nElementAt()\u6269\u5c55\u65b9\u6cd5\u4ece\u6307\u5b9a\u4f4d\u7f6e\u7684\u6570\u7ec4\u4e2d\u68c0\u7d22\u5143\u7d20\uff0c\u662fNET 6\/C#10\u4e2d\u7684\u65b0\u589e\u529f\u80fd\uff0c\u652f\u6301\u4f7f\u7528index from end\u8fd0\u7b97\u7b26\u6765\u83b7\u53d6\u8ddd\u79bb\u6570\u7ec4\u672b\u5c3e\u6307\u5b9a\u8ddd\u79bb\u7684\u5143\u7d20\u3002\u4ee5\u4e0b\u4ee3\u7801\u4ece\u5217\u8868\u4e2d\u83b7\u53d6\u5012\u6570\u7b2c\u4e8c\u4e2a\u5143\u7d20\uff1a<\/p>\n<p><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nConsole.WriteLine(&quot;***** Fun with Arrays *****&quot;);\n\nstring&#x5B;] gothicBands = { &quot;Tones on Tail&quot;, &quot;Bauhaus&quot;, &quot;Sisters of Mercy&quot; };\n\nvar band = gothicBands.ElementAt(^2);\nConsole.WriteLine(band);\n<\/pre>\n<\/p>\n<blockquote><p>\n\u25a0 Note  support for using Index and Range parameters has been added to LinQ. see Chapter 13 for more information.<br \/>\n\u6ce8\u610f LinQ \u4e2d\u6dfb\u52a0\u4e86\u5bf9\u4f7f\u7528\u7d22\u5f15\u548c\u8303\u56f4\u53c2\u6570\u7684\u652f\u6301\u3002\u6709\u5173\u8be6\u7ec6\u4fe1\u606f\uff0c\u8bf7\u53c2\u9605\u7b2c 13 \u7ae0\u3002\n<\/p><\/blockquote>\n<h2>Understanding Methods<\/h2>\n<p>\u4e86\u89e3\u65b9\u6cd5<\/p>\n<p>Let\u2019s examine the details of defining methods. Methods are defined by an access modifier and return type (or void for no return type) and may or may not take parameters. A method that returns a value to the caller is commonly referred to as a function, while methods that do not return a value are commonly referred to as methods.<br \/>\n\u8ba9\u6211\u4eec\u7814\u7a76\u4e00\u4e0b\u5b9a\u4e49\u65b9\u6cd5\u7684\u7ec6\u8282\u3002\u65b9\u6cd5\u7531\u8bbf\u95ee\u4fee\u9970\u7b26\u548c\u8fd4\u56de\u7c7b\u578b\uff08\u6216void\u8868\u793a\u65e0\u8fd4\u56de\u7c7b\u578b\uff09\u5b9a\u4e49\uff0c\u53ef\u4ee5\u63a5\u53d7\u4e5f\u53ef\u4ee5\u4e0d\u63a5\u53d7\u53c2\u6570\u3002\u5c06\u503c\u8fd4\u56de\u7ed9\u8c03\u7528\u65b9\u7684\u65b9\u6cd5\u901a\u5e38\u79f0\u4e3a\u51fd\u6570\uff0c\u800c\u4e0d\u8fd4\u56de\u503c\u7684\u65b9\u6cd5\u901a\u5e38\u88ab\u79f0\u4e3a\u65b9\u6cd5\u3002<\/p>\n<blockquote><p>\n\u25a0Note  access modifiers for methods (and classes) are covered in Chapter 5. method parameters are covered in the next section.<br \/>\n\u6ce8\u610f \u65b9\u6cd5\uff08\u548c\u7c7b\uff09\u7684\u8bbf\u95ee\u4fee\u9970\u7b26\u5728\u7b2c5\u7ae0\u4e2d\u4ecb\u7ecd\u3002\u65b9\u6cd5\u53c2\u6570\u5c06\u5728\u4e0b\u4e00\u8282\u4e2d\u4ecb\u7ecd\u3002\n<\/p><\/blockquote>\n<p>At this point in the text, each of your methods has the following basic format:<br \/>\n\u6bcf\u4e2a\u65b9\u6cd5\u90fd\u5177\u6709\u4ee5\u4e0b\u57fa\u672c\u683c\u5f0f\uff1a<\/p>\n<p><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n\/\/ Recall that static methods can be called directly\n\/\/ \u9759\u6001\u65b9\u6cd5\u53ef\u4ee5\u76f4\u63a5\u8c03\u7528\n\/\/ without creating a class instance.\n\/\/ \u65e0\u9700\u521b\u5efa\u7c7b\u5b9e\u4f8b\u3002\n\n\/\/ static returnType MethodName(parameter list) { \/* Implementation *\/ }\n\/\/ \u9759\u6001 \u8fd4\u56de\u7c7b\u578b \u65b9\u6cd5\u540d\u79f0\uff08\u53c2\u6570\u5217\u8868\uff09 { \/* \u5177\u4f53\u5b9e\u73b0\u65b9\u6cd5\u7684\u8bed\u53e5 *\/ }\n\nstatic int Add(int x, int y)\n{\n    return x + y;\n}\n<\/pre>\n<\/p>\n<p>As you will see over the next several chapters, methods can be implemented within the scope of classes, structures, or (new in C# 8) interfaces.<br \/>\n\u6b63\u5982\u60a8\u5c06\u5728\u63a5\u4e0b\u6765\u7684\u51e0\u7ae0\u4e2d\u770b\u5230\u7684\uff0c\u65b9\u6cd5\u53ef\u4ee5\u5728\u7c7b\u3001\u7ed3\u6784\u6216\uff08C# 8 \u4e2d\u7684\u65b0\u529f\u80fd\uff09\u63a5\u53e3\u7684\u8303\u56f4\u5185\u5b9e\u73b0\u3002<\/p>\n<h2>Understanding Expression-Bodied Members<\/h2>\n<p>\u7406\u89e3Expression Bodied\u6210\u5458<\/p>\n<p>You already learned about simple methods that return values, such as the Add() method. C# 6 introduced expression-bodied members that shorten the syntax for single-line methods. For example, Add() can be rewritten using the following syntax:<br \/>\n\u60a8\u5df2\u7ecf\u4e86\u89e3\u4e86\u8fd4\u56de\u503c\u7684\u7b80\u5355\u65b9\u6cd5\uff0c\u4f8b\u5982Add()\u65b9\u6cd5\u3002C#\u5f15\u5165\u4e86\u8868\u8fbe\u5f0f\u4f53\u6210\u5458\uff0c\u7f29\u77ed\u4e86\u5355\u884c\u65b9\u6cd5\u7684\u8bed\u6cd5\u3002\u4f8b\u5982\uff0c\u53ef\u4ee5\u4f7f\u7528\u4ee5\u4e0b\u8bed\u6cd5\u91cd\u5199Add\uff08\uff09\uff1a<br \/>\nExpression-bodied \u65b9\u6cd5\u662f\u80fd\u7b80\u5316\u4ee3\u7801\u7684\u7279\u6027\uff0c\u5c06LAMBDA\u8868\u8fbe\u5f0f\u7684\u7528\u6cd5\u6269\u5c55\u5230\u65b9\u6cd5\u4e0a\u3002<\/p>\n<p><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nstatic int Add(int x, int y) =&gt; x + y;\n<\/pre>\n<\/p>\n<p>This is what is commonly referred to as syntactic sugar, meaning that the generated IL is no different. It is just another way to write the method. Some find it easier to read, and others do not, so the choice is yours (or your team\u2019s) which style you prefer.<br \/>\n\u8fd9\u5c31\u662f\u901a\u5e38\u6240\u8bf4\u7684\u53e5\u6cd5\u7cd6\uff0c\u4e24\u79cd\u5199\u6cd5\u751f\u6210\u7684IL\u662f\u4e00\u6837\u7684\u3002\u8fd9\u53ea\u662f\u7f16\u5199\u65b9\u6cd5\u7684\u53e6\u4e00\u79cd\u65b9\u5f0f\u3002\u6709\u4e9b\u4eba\u89c9\u5f97\u5b83\u66f4\u5bb9\u6613\u9605\u8bfb\uff0c\u800c\u53e6\u4e00\u4e9b\u4eba\u5219\u4e0d\u7136\uff0c\u6240\u4ee5\u4f60\u53ef\u4ee5\u9009\u62e9\u4f60\u559c\u6b22\u7684\u98ce\u683c\u3002<\/p>\n<blockquote><p>\n\u25a0 Note  Don\u2019t be alarmed by the =&gt; operator. this is a lambda operation, which is covered in detail in Chapter 12. that chapter also explains exactly how expression-bodied members work. For now, just consider them a shortcut to writing single-line statements.<br \/>\n\u6ce8\u610f \u4e0d\u8981\u88ab =&gt; \u8fd0\u7b97\u7b26\u5413\u5230\u3002 \u8fd9\u662f\u4e00\u4e2a lambda \u64cd\u4f5c\uff0c\u7b2c 12 \u7ae0\u5c06\u8be6\u7ec6\u4ecb\u7ecd\u3002 \u8fd9\u4e00\u7ae0\u8fd8\u786e\u5207\u5730\u89e3\u91ca\u4e86Expression-bodied\u6210\u5458\u662f\u5982\u4f55\u5de5\u4f5c\u7684\u3002\u73b0\u5728\uff0c\u53ea\u9700\u5c06\u5b83\u4eec\u89c6\u4e3a\u7f16\u5199\u5355\u884c\u8bed\u53e5\u7684\u5feb\u6377\u65b9\u5f0f\u3002\n<\/p><\/blockquote>\n<h2>Understanding Local Functions (New 7.0, Updated 9.0)<\/h2>\n<p>\u4e86\u89e3\u672c\u5730\u51fd\u6570<\/p>\n<p>A feature introduced in C# 7.0 is the ability to create methods within methods, referred to officially as local functions. A local function is a function declared inside another function, must be private, with C# 8.0 can be static (see the next section), and does not support overloading. Local functions do support nesting: a local function can have a local function declared inside it.<br \/>\nC#7.0\u4e2d\u5f15\u5165\u7684\u4e00\u4e2a\u529f\u80fd\u662f\u80fd\u591f\u5728\u65b9\u6cd5\u4e2d\u521b\u5efa\u65b9\u6cd5\uff0c\u6b63\u5f0f\u79f0\u4e3a\u672c\u5730\u51fd\u6570\u3002\u672c\u5730\u51fd\u6570\u662f\u5728\u53e6\u4e00\u4e2a\u51fd\u6570\u5185\u90e8\u58f0\u660e\u7684\u51fd\u6570\uff0c\u5fc5\u987b\u662f\u79c1\u6709\u7684\uff0c\u4f7f\u7528C#8.0\u53ef\u4ee5\u662f\u9759\u6001\u7684\uff08\u8bf7\u53c2\u9605\u4e0b\u4e00\u8282\uff09\uff0c\u5e76\u4e14\u4e0d\u652f\u6301\u91cd\u8f7d\u3002\u672c\u5730\u51fd\u6570\u652f\u6301\u5d4c\u5957\uff1a\u672c\u5730\u51fd\u6570\u5185\u90e8\u53ef\u4ee5\u58f0\u660e\u4e00\u4e2a\u672c\u5730\u51fd\u6570\u3002<\/p>\n<p>To see how this works, create a new Console Application project named FunWithLocalFunctions. As an example, let\u2019s say you want to extend the Add() example used previously to include validation of the inputs. There are many ways to accomplish this, and one simple way is to add the validation directly into the Add() method. Let\u2019s go with that and update the previous example to the following (the comment representing validation logic):<br \/>\n\u8981\u4e86\u89e3\u8fd9\u662f\u5982\u4f55\u5de5\u4f5c\u7684\uff0c\u8bf7\u521b\u5efa\u4e00\u4e2a\u540d\u4e3aFunWithLocalFunctions\u7684\u65b0\u63a7\u5236\u53f0\u5e94\u7528\u7a0b\u5e8f\u9879\u76ee\u3002\u4e3e\u4e2a\u4f8b\u5b50\uff0c\u5047\u8bbe\u60a8\u60f3\u6269\u5c55\u4ee5\u524d\u4f7f\u7528\u7684Add\uff08\uff09\u793a\u4f8b\uff0c\u589e\u52a0\u8f93\u5165\u7684\u9a8c\u8bc1\u3002\u6709\u5f88\u591a\u65b9\u6cd5\u53ef\u4ee5\u5b9e\u73b0\u8fd9\u4e00\u70b9\uff0c\u5176\u4e2d\u4e00\u79cd\u7b80\u5355\u7684\u65b9\u6cd5\u662f\u5c06\u9a8c\u8bc1\u76f4\u63a5\u6dfb\u52a0\u5230add\uff08\uff09\u65b9\u6cd5\u4e2d\u3002\u8ba9\u6211\u4eec\u7ee7\u7eed\uff0c\u5e76\u5c06\u524d\u9762\u7684\u793a\u4f8b\u66f4\u65b0\u4e3a\u4ee5\u4e0b\u5185\u5bb9\uff08\u8868\u793a\u9a8c\u8bc1\u903b\u8f91\u7684\u6ce8\u91ca\uff09\uff1a<\/p>\n<p><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nstatic int Add(int x, int y)\n{\n    \/\/ Do some validation here\n    \/\/ \u8fd9\u91cc\u505a\u4e00\u4e9b\u9a8c\u8bc1\n\n    \/\/ \u7136\u540e\u8fd4\u56de\u503c\uff08\u8f93\u51fa\u7ed3\u679c\uff09\n    return x + y;\n}\n<\/pre>\n<\/p>\n<p>As you can see, there are no big changes. There is just a comment indicating that real code should do something. What if you wanted to separate the actual reason for the method (returning the sum of the arguments) from the validation of the arguments? You could create additional methods and call them from the Add() method. But that would require creating another method just for use by one other method. Maybe that\u2019s overkill. Local functions allow you to do the validation first and then encapsulate the real goal of the method defined inside the AddWrapper() method, as shown here:<br \/>\n\u5982\u60a8\u6240\u89c1\uff0c\u6ca1\u6709\u4ec0\u4e48\u5927\u7684\u53d8\u5316\u3002\u53ea\u6709\u4e00\u6761\u6ce8\u91ca\u8868\u660e\u771f\u6b63\u7684\u4ee3\u7801\u5e94\u8be5\u505a\u4e9b\u4ec0\u4e48\u3002\u5982\u679c\u60a8\u60f3\u5c06\u65b9\u6cd5\u7684\u5b9e\u9645\u539f\u56e0\uff08\u8fd4\u56de\u53c2\u6570\u7684\u603b\u548c\uff09\u4e0e\u53c2\u6570\u7684\u9a8c\u8bc1\u5206\u5f00\uff0c\u8be5\u600e\u4e48\u529e\uff1f\u60a8\u53ef\u4ee5\u521b\u5efa\u5176\u4ed6\u65b9\u6cd5\u5e76\u4eceAdd\uff08\uff09\u65b9\u6cd5\u8c03\u7528\u5b83\u4eec\u3002\u4f46\u8fd9\u5c06\u9700\u8981\u521b\u5efa\u53e6\u4e00\u4e2a\u65b9\u6cd5\uff0c\u53ea\u4f9b\u53e6\u4e00\u79cd\u65b9\u6cd5\u4f7f\u7528\u3002\u4e5f\u8bb8\u8fd9\u662f\u77eb\u6789\u8fc7\u6b63\u3002\u672c\u5730\u51fd\u6570\u5141\u8bb8\u60a8\u9996\u5148\u8fdb\u884c\u9a8c\u8bc1\uff0c\u7136\u540e\u5c06\u5b9a\u4e49\u7684\u5b9e\u9645\u76ee\u6807\u7684\u65b9\u6cd5\u5c01\u88c5AddWrapper()\u65b9\u6cd5\u4e2d\uff0c\u5982\u4e0b\u6240\u793a\uff1a<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nstatic int AddWrapper(int x, int y)\n{\n    \/\/Do some validation here\n    \/\/ \u8fd9\u91cc\u505a\u4e00\u4e9b\u9a8c\u8bc1\n\n    return Add();\n    int Add()\n    {\n        return x + y;\n    }\n}\n<\/pre>\n<\/p>\n<p>The contained Add() method can be called only from the wrapping AddWrapper() method. So, the question I am sure you are thinking is, \u201cWhat did this buy me?\u201d The answer for this specific example, quite simply, is little (if anything). But what if AddWrapper() needed to execute the Add() function from multiple places? Now you should start to see the benefit of having a local function for code reuse that is not exposed outside of where it is needed. You will see even more benefit gained with local functions when we cover custom iterator methods (Chapter 8) and asynchronous methods (Chapter 15).<br \/>\n\u53ea\u80fd\u4ece\u5305\u88c5AddWrapper()\u65b9\u6cd5\u8c03\u7528\u5305\u542b\u7684Add()\u65b9\u6cd5\u3002\u6240\u4ee5\uff0c\u6211\u76f8\u4fe1\u4f60\u5728\u60f3\u7684\u95ee\u9898\u662f\uff0c\u201c\u8fd9\u7ed9\u6211\u4e70\u4e86\u4ec0\u4e48\uff1f\u201d\u8fd9\u4e2a\u5177\u4f53\u4f8b\u5b50\u7684\u7b54\u6848\u5f88\u7b80\u5355\uff0c\u5f88\u5c11\uff08\u5982\u679c\u6709\u7684\u8bdd\uff09\u3002\u4f46\u662f\uff0c\u5982\u679cAddWrapper\uff08\uff09\u9700\u8981\u4ece\u591a\u4e2a\u4f4d\u7f6e\u6267\u884cAdd\uff08\uff09\u51fd\u6570\uff0c\u8be5\u600e\u4e48\u529e\uff1f\u73b0\u5728\uff0c\u60a8\u5e94\u8be5\u5f00\u59cb\u770b\u5230\u62e5\u6709\u4e00\u4e2a\u7528\u4e8e\u4ee3\u7801\u91cd\u7528\u7684\u672c\u5730\u51fd\u6570\u7684\u597d\u5904\uff0c\u8be5\u51fd\u6570\u4e0d\u4f1a\u5728\u9700\u8981\u5b83\u7684\u5730\u65b9\u4e4b\u5916\u516c\u5f00\u3002\u5f53\u6211\u4eec\u4ecb\u7ecd\u81ea\u5b9a\u4e49\u8fed\u4ee3\u5668\u65b9\u6cd5\uff08\u7b2c8\u7ae0\uff09\u548c\u5f02\u6b65\u65b9\u6cd5\uff08\u7b2c15\u7ae0\uff09\u65f6\uff0c\u60a8\u5c06\u770b\u5230\u672c\u5730\u51fd\u6570\u83b7\u5f97\u7684\u66f4\u591a\u597d\u5904\u3002<\/p>\n<blockquote><p>\n\u25a0 Note the AddWrapper() local function is an example of local function with a nested local function. recall that functions declared in top-level statements are created as local functions. the Add() local function is in the AddWrapper() local function. this capability typically is not used outside of teaching examples, but if you ever need to nest local functions, you know that C# supports it.<br \/>\nAddWrapper\uff08\uff09\u672c\u5730\u51fd\u6570\u662f\u4e00\u4e2a\u5e26\u6709\u5d4c\u5957\u672c\u5730\u51fd\u6570\u7684\u5c40\u90e8\u51fd\u6570\u793a\u4f8b\u3002\u8bf7\u8bb0\u4f4f\uff0c\u5728\u9876\u7ea7\u8bed\u53e5\u4e2d\u58f0\u660e\u7684\u51fd\u6570\u662f\u4f5c\u4e3a\u672c\u5730\u51fd\u6570\u521b\u5efa\u7684\u3002Add\uff08\uff09\u5c40\u90e8\u51fd\u6570\u4f4d\u4e8eAddWrapper\uff08\uff09\u5c40\u90e8\u529f\u80fd\u4e2d\u3002\u8fd9\u79cd\u529f\u80fd\u901a\u5e38\u4e0d\u4f1a\u5728\u6559\u5b66\u793a\u4f8b\u4e4b\u5916\u4f7f\u7528\uff0c\u4f46\u5982\u679c\u60a8\u9700\u8981\u5d4c\u5957\u672c\u5730\u51fd\u6570\uff0c\u60a8\u77e5\u9053C#\u652f\u6301\u5b83\u3002\n<\/p><\/blockquote>\n<p>C# 9.0 updated local functions to allow for adding attributes to a local function, its parameters, and its type parameters, as in the following example (do not worry about the NotNullWhen attribute, which will be covered later in this chapter):<br \/>\nC#9.0\u66f4\u65b0\u4e86\u672c\u5730\u51fd\u6570\uff0c\u5141\u8bb8\u5411\u672c\u5730\u51fd\u6570\u3001\u5176\u53c2\u6570\u548c\u7c7b\u578b\u53c2\u6570\u6dfb\u52a0\u5c5e\u6027\uff0c\u5982\u4e0b\u4f8b\u6240\u793a\uff08\u4e0d\u8981\u62c5\u5fc3NotNullWhen\u5c5e\u6027\uff0c\u8fd9\u5c06\u5728\u672c\u7ae0\u7a0d\u540e\u4ecb\u7ecd\uff09\uff1a<\/p>\n<p>C# 9.0 \u66f4\u65b0\u4e86\u672c\u5730\u51fd\u6570\uff0c\u4ee5\u5141\u8bb8\u5411\u672c\u5730\u51fd\u6570\u3001\u5176\u53c2\u6570\u548c\u7c7b\u578b\u53c2\u6570\u6dfb\u52a0\u5c5e\u6027\uff0c\u5982\u4ee5\u4e0b\u793a\u4f8b\u6240\u793a\uff08\u4e0d\u7528\u62c5\u5fc3 NotNullWhen \u5c5e\u6027\uff0c\u672c\u7ae0\u7a0d\u540e\u5c06\u4ecb\u7ecd\u8be5\u5c5e\u6027\uff09\uff1a<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n#nullable enable\nprivate static void Process(string?&#x5B;] lines, string mark)\n{\n    foreach (var line in lines)\n    {\n        if (IsValid(line))\n        {\n            \/\/ Processing logic...\n        }\n    }\n    bool IsValid(&#x5B;NotNullWhen(true)] string? line)\n    {\n        return !string.IsNullOrEmpty(line) &amp;&amp; line.Length &gt;= mark.Length;\n    }\n}\n<\/pre>\n<\/p>\n<h2>Understanding Static Local Functions (New 8.0)<\/h2>\n<p>\u4e86\u89e3\u9759\u6001\u672c\u5730\u51fd\u6570\uff08\u65b0 8.0\uff09<\/p>\n<p>An improvement to local functions that was introduced in C# 8 is the ability to declare a local function as static. In the previous example, the local Add() function was referencing the variables from the main function directly. This could cause unexpected side effects, since the local function can change the values of the variables.<br \/>\nC#8\u4e2d\u5f15\u5165\u4e00\u4e2a\u6539\u8fdb\u7684\u672c\u5730\u51fd\u6570\u3002\u80fd\u591f\u5c06\u672c\u5730\u51fd\u6570\u58f0\u660e\u4e3a\u9759\u6001\u51fd\u6570\u3002\u5728\u524d\u9762\u7684\u793a\u4f8b\u4e2d\uff0c\u672c\u5730Add()\u51fd\u6570\u76f4\u63a5\u5f15\u7528\u4e3b\u51fd\u6570\u4e2d\u7684\u53d8\u91cf\u3002\u8fd9\u53ef\u80fd\u4f1a\u5bfc\u81f4\u610f\u5916\u7684\u526f\u4f5c\u7528\uff0c\u56e0\u4e3a\u672c\u5730\u51fd\u6570\u53ef\u4ee5\u66f4\u6539\u53d8\u91cf\u7684\u503c\u3002<\/p>\n<p>To see this in action, create a new method called AddWrapperWithSideEffect(), as shown here:<br \/>\n\u82e5\u8981\u67e5\u770b\u6b64\u64cd\u4f5c\u7684\u5b9e\u9645\u6548\u679c\uff0c\u8bf7\u521b\u5efa\u4e00\u4e2a\u540d\u4e3aAddWrapperWithSideEffect()\u7684\u65b0\u65b9\u6cd5\uff0c\u5982\u4e0b\u6240\u793a\uff1a<\/p>\n<p><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nstatic int AddWrapperWithSideEffect(int x, int y)\n{\n    \/\/Do some validation here\n    return Add();\n    int Add()\n    {\n        x += 1;\n        return x + y;\n    }\n}\n<\/pre>\n<\/p>\n<p>Of course, this example is so simple, it probably would not happen in real code. To prevent this type of mistake, add the static modifier to the local function. This prevents the local function from accessing the parent method variables directly, and this causes the compiler exception CS8421, \u201cA static local function cannot contain a reference to \u2018<variable name>.\u2019\u201d<br \/>\n\u5f53\u7136\uff0c\u8fd9\u4e2a\u4f8b\u5b50\u5f88\u7b80\u5355\uff0c\u5b83\u53ef\u80fd\u4e0d\u4f1a\u53d1\u751f\u5728\u771f\u5b9e\u7684\u4ee3\u7801\u4e2d\u3002\u4e3a\u4e86\u9632\u6b62\u8fd9\u79cd\u7c7b\u578b\u7684\u9519\u8bef\uff0c\u8bf7\u5c06\u9759\u6001\u4fee\u9970\u7b26\u6dfb\u52a0\u5230\u672c\u5730\u51fd\u6570\u4e2d\u3002\u8fd9\u4f1a\u963b\u6b62\u672c\u5730\u51fd\u6570\u76f4\u63a5\u8bbf\u95ee\u7236\u65b9\u6cd5\u53d8\u91cf\uff0c\u5e76\u5bfc\u81f4\u7f16\u8bd1\u5668\u5f02\u5e38CS8421\uff0c\u201c\u9759\u6001\u672c\u5730\u51fd\u6570\u4e0d\u80fd\u5305\u542b\u5bf9'<variable name>'\u7684\u5f15\u7528\u3002\u201d<\/p>\n<p>The improved version of the previous method is shown here:<br \/>\n\u4ee5\u524d\u65b9\u6cd5\u7684\u6539\u8fdb\u7248\u672c\u5982\u4e0b\u6240\u793a\uff1a<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nstatic int AddWrapperWithStatic(int x, int y)\n{\n    \/\/Do some validation here\n    return Add(x, y);\n    static int Add(int x, int y)\n    {\n        return x + y;\n    }\n}\n<\/pre>\n<\/p>\n<h3>Understanding Method Parameters<\/h3>\n<p>\u4e86\u89e3\u65b9\u6cd5\u53c2\u6570<\/p>\n<p>Method parameters are used to pass data into a method call. Over the next several sections, you will learn the details of how methods (and their callers) treat parameters.<br \/>\n\u65b9\u6cd5\u53c2\u6570\u7528\u4e8e\u5c06\u6570\u636e\u4f20\u9012\u5230\u8c03\u7528\u65b9\u6cd5\u4e2d\u3002\u5728\u63a5\u4e0b\u6765\u7684\u51e0\u8282\u4e2d\uff0c\u60a8\u5c06\u8be6\u7ec6\u4e86\u89e3\u65b9\u6cd5\uff08\u53ca\u5176\u8c03\u7528\u65b9\uff09\u5982\u4f55\u5904\u7406\u53c2\u6570\u3002<\/p>\n<h3>Understanding Method Parameter Modifiers<\/h3>\n<p>\u4e86\u89e3\u65b9\u6cd5\u53c2\u6570\u4fee\u9970\u7b26<\/p>\n<p>The default way a parameter is sent into a function is by value. Simply put, if you do not mark an argument with a parameter modifier, a copy of the data is passed into the function. As explained later in this chapter, exactly what is copied will depend on whether the parameter is a value type or a reference type.<br \/>\n\u5c06\u53c2\u6570\u53d1\u9001\u5230\u51fd\u6570\u7684\u9ed8\u8ba4\u65b9\u5f0f\u662f\u6309\u503c\u53d1\u9001\u3002\u7b80\u5355\u5730\u8bf4\uff0c\u5982\u679c\u4e0d\u4f7f\u7528\u53c2\u6570\u4fee\u9970\u7b26\u6807\u8bb0\u53c2\u6570\uff0c\u5219\u4f1a\u5c06\u6570\u636e\u7684\u526f\u672c\u4f20\u9012\u5230\u51fd\u6570\u4e2d\u3002\u5982\u672c\u7ae0\u7a0d\u540e\u6240\u8ff0\uff0c\u590d\u5236\u7684\u5185\u5bb9\u5c06\u53d6\u51b3\u4e8e\u53c2\u6570\u662f\u503c\u7c7b\u578b\u8fd8\u662f\u5f15\u7528\u7c7b\u578b\u3002<\/p>\n<p>While the definition of a method in C# is quite straightforward, you can use a handful of methods to control how arguments are passed to a method, as listed in Table 4-2.<br \/>\n\u867d\u7136C#\u4e2d\u65b9\u6cd5\u7684\u5b9a\u4e49\u975e\u5e38\u7b80\u5355\uff0c\u4f46\u60a8\u53ef\u4ee5\u4f7f\u7528\u4e00\u4e9b\u65b9\u6cd5\u6765\u63a7\u5236\u53c2\u6570\u5982\u4f55\u4f20\u9012\u7ed9\u65b9\u6cd5\uff0c\u5982\u88684-2\u6240\u793a\u3002<\/p>\n<p>Table 4-2. C# Parameter Modifiers<br \/>\n\u8868 4-2. C# \u53c2\u6570\u4fee\u9970\u7b26<\/p>\n<table>\n<tr>\n<td>Parameter Modifier<\/td>\n<td>Meaning in Life<\/td>\n<\/tr>\n<tr>\n<td>(None)<\/td>\n<td>If a value type parameter is not marked with a modifier, it is assumed to be passed by value, meaning the called method receives a copy of the original data. Reference types without a modifier are passed in by reference.<br \/>\n        \u5982\u679c\u503c\u7c7b\u578b\u53c2\u6570\u672a\u6807\u6709\u4fee\u9970\u7b26\uff0c\u5219\u5047\u5b9a\u5b83\u662f\u6309\u503c\u4f20\u9012\u7684\uff0c\u8fd9\u610f\u5473\u7740\u88ab\u8c03\u7528\u7684\u65b9\u6cd5\u63a5\u6536\u539f\u59cb\u6570\u636e\u7684\u526f\u672c\u3002\u4e0d\u5e26\u4fee\u9970\u7b26\u7684\u5f15\u7528\u7c7b\u578b\u901a\u8fc7\u5f15\u7528\u4f20\u5165\u3002<\/td>\n<\/tr>\n<tr>\n<td>out<\/td>\n<td>Output parameters must be assigned by the method being called and, therefore, are passed by reference. If the called method fails to assign output parameters, you are issued a compiler error.<br \/>\n        \u8f93\u51fa\u53c2\u6570\u5fc5\u987b\u7531\u88ab\u8c03\u7528\u7684\u65b9\u6cd5\u5206\u914d\uff0c\u56e0\u6b64\u901a\u8fc7\u5f15\u7528\u4f20\u9012\u3002\u5982\u679c\u8c03\u7528\u7684\u65b9\u6cd5\u65e0\u6cd5\u5206\u914d\u8f93\u51fa\u53c2\u6570\uff0c\u5219\u4f1a\u5411\u60a8\u53d1\u51fa\u7f16\u8bd1\u5668\u9519\u8bef\u3002<\/td>\n<\/tr>\n<tr>\n<td>ref<\/td>\n<td>The value is initially assigned by the caller and may be optionally modified by the called method (as the data is also passed by reference). No compiler error is generated if the called method fails to assign a\u00a0ref\u00a0parameter.<br \/>\n        \u8be5\u503c\u6700\u521d\u7531\u8c03\u7528\u8005\u5206\u914d\uff0c\u5e76\u4e14\u53ef\u4ee5\u7531\u88ab\u8c03\u7528\u7684\u65b9\u6cd5\u9009\u62e9\u6027\u5730\u4fee\u6539\uff08\u56e0\u4e3a\u6570\u636e\u4e5f\u662f\u901a\u8fc7\u5f15\u7528\u4f20\u9012\u7684\uff09\u3002\u5982\u679c\u88ab\u8c03\u7528\u7684\u65b9\u6cd5\u672a\u80fd\u5206\u914dref\u53c2\u6570\uff0c\u5219\u4e0d\u4f1a\u751f\u6210\u7f16\u8bd1\u5668\u9519\u8bef\u3002<\/td>\n<\/tr>\n<tr>\n<td>in<\/td>\n<td>New in C# 7.2, the\u00a0in\u00a0modifier indicates that a\u00a0ref\u00a0parameter is read-only by the called method.<br \/>\n        C# 7.2 \u4e2d\u7684\u65b0\u589e\u529f\u80fd\uff0cin \u4fee\u9970\u7b26\u6307\u793a ref \u53c2\u6570\u88ab\u8c03\u7528\u7684\u65b9\u6cd5\u53ea\u8bfb\u3002<\/td>\n<\/tr>\n<tr>\n<td>params<\/td>\n<td>This parameter modifier allows you to send in a variable number of arguments as a single logical parameter. A method can have only a single\u00a0params\u00a0modifier, and it must be the final parameter of the method. You might not need to use the\u00a0params\u00a0modifier all too often;<br \/>\n        however, be aware that numerous methods within the base class libraries do make use of this C# language feature.<br \/>\n        \u6b64\u53c2\u6570\u4fee\u9970\u7b26\u5141\u8bb8\u60a8\u5c06\u53ef\u53d8\u6570\u91cf\u7684\u53c2\u6570\u4f5c\u4e3a\u5355\u4e2a\u903b\u8f91\u53c2\u6570\u53d1\u9001\u3002\u4e00\u4e2a\u65b9\u6cd5\u53ea\u80fd\u6709\u4e00\u4e2aparams\u4fee\u9970\u7b26\uff0c\u5e76\u4e14\u5b83\u5fc5\u987b\u662f\u8be5\u65b9\u6cd5\u7684\u6700\u7ec8\u53c2\u6570\u3002\u60a8\u53ef\u80fd\u4e0d\u9700\u8981\u7ecf\u5e38\u4f7f\u7528params\u4fee\u9970\u7b26\uff1b\u4f46\u662f\uff0c\u8bf7\u6ce8\u610f\uff0c\u57fa\u7c7b\u5e93\u4e2d\u7684\u8bb8\u591a\u65b9\u6cd5\u786e\u5b9e\u4f7f\u7528\u4e86\u8fd9\u4e2a\u7279\u6027\u3002<\/td>\n<\/tr>\n<\/table>\n<p>To illustrate the use of these keywords, create a new Console Application project named FunWithMethods. Now, let\u2019s walk through the role of each keyword.<br \/>\n\u4e3a\u4e86\u8bf4\u660e\u8fd9\u4e9b\u5173\u952e\u5b57\u7684\u7528\u6cd5\uff0c\u8bf7\u521b\u5efa\u4e00\u4e2a\u540d\u4e3a FunWithMethods \u7684\u65b0\u63a7\u5236\u53f0\u5e94\u7528\u7a0b\u5e8f\u9879\u76ee\u3002\u73b0\u5728\uff0c\u8ba9\u6211\u4eec\u6765\u770b\u770b\u6bcf\u4e2a\u5173\u952e\u5b57\u7684\u4f5c\u7528\u3002<\/p>\n<h2>Understanding the Default Parameter-Passing Behavior<\/h2>\n<p>\u4e86\u89e3\u9ed8\u8ba4\u53c2\u6570\u4f20\u9012\u884c\u4e3a<\/p>\n<p>When a parameter does not have a modifier, the behavior for value types is to pass in the parameter by value and for reference types is to pass in the parameter by reference.<br \/>\n\u5f53\u53c2\u6570\u6ca1\u6709\u4fee\u9970\u7b26\u65f6\uff0c\u503c\u7c7b\u578b\u7684\u884c\u4e3a\u662f\u6309\u503c\u4f20\u5165\u53c2\u6570\uff0c\u5f15\u7528\u7c7b\u578b\u7684\u884c\u4e3a\u662f\u6309\u5f15\u7528\u4f20\u5165\u53c2\u6570\u3002<\/p>\n<blockquote><p>\n\u25a0 Note  Value types and reference types are covered later in this chapter.<br \/>\n\u6ce8\u91ca \u672c\u7ae0\u7a0d\u540e\u5c06\u4ecb\u7ecd\u503c\u7c7b\u578b\u548c\u5f15\u7528\u7c7b\u578b\u3002\n<\/p><\/blockquote>\n<h2>The Default Behavior for Value Types<\/h2>\n<p>\u503c\u7c7b\u578b\u7684\u9ed8\u8ba4\u884c\u4e3a<\/p>\n<p>The default way a value type parameter is sent into a function is by value. Simply put, if you do not mark the argument with a modifier, a copy of the data is passed into the function. Add the following method to the Program.cs file that operates on two numerical data types passed by value:<br \/>\n\u5c06\u503c\u7c7b\u578b\u53c2\u6570\u53d1\u9001\u5230\u51fd\u6570\u7684\u9ed8\u8ba4\u65b9\u5f0f\u662f\u6309\u503c\u53d1\u9001\u3002\u7b80\u5355\u5730\u8bf4\uff0c\u5982\u679c\u4e0d\u4f7f\u7528\u4fee\u9970\u7b26\u6807\u8bb0\u53c2\u6570\uff0c\u5219\u4f1a\u5c06\u6570\u636e\u7684\u526f\u672c\u4f20\u9012\u5230\u51fd\u6570\u4e2d\u3002\u5c06\u4ee5\u4e0b\u65b9\u6cd5\u6dfb\u52a0\u5230Program.cs\u6587\u4ef6\u4e2d\uff0c\u5bf9\u901a\u8fc7\u503c\u4f20\u9012\u7684\u4e24\u79cd\u6570\u503c\u6570\u636e\u7c7b\u578b\u8fdb\u884c\u64cd\u4f5c\uff1a<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n\/\/ Value type arguments are passed by value by default.\n\/\/ \u9ed8\u8ba4\u60c5\u51b5\u4e0b\uff0c\u503c\u7c7b\u578b\u53c2\u6570\u6309\u503c\u4f20\u9012\u3002\nstatic int Add(int x, int y)\n{\n    int ans = x + y;\n    \/\/ Caller will not see these changes\n    \/\/ \u8c03\u7528\u8005\u770b\u4e0d\u5230\u8fd9\u4e9b\n    \/\/ as you are modifying a copy of the original data.\n    \/\/ \u4f60\u4fee\u6539\u7684\u539f\u59cb\u6570\u636e\u526f\u672c\n\n    x = 10000;\n    y = 88888;\n    return ans;\n}\n<\/pre>\n<\/p>\n<p>Numerical data falls under the category of value types. Therefore, if you change the values of the parameters within the scope of the member, the caller is blissfully unaware, given that you are changing the values on a copy of the caller\u2019s original data.<br \/>\n\u6570\u503c\u6570\u636e\u5c5e\u4e8e\u503c\u7c7b\u578b\u7684\u8303\u7574\u3002\u56e0\u6b64\uff0c\u5982\u679c\u60a8\u5728\u6210\u5458\u7684\u8303\u56f4\u5185\u66f4\u6539\u53c2\u6570\u7684\u503c\uff0c\u90a3\u4e48\u8c03\u7528\u8005\u4e0d\u77e5\u9053\uff0c\u56e0\u4e3a\u60a8\u6b63\u5728\u66f4\u6539\u8c03\u7528\u8005\u539f\u59cb\u6570\u636e\u526f\u672c\u4e0a\u7684\u503c\u3002<\/p>\n<p><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nConsole.WriteLine(&quot;***** Fun with Methods *****\\n&quot;);\n\n\/\/ Pass two variables in by value.\n\/\/ \u6309\u503c\u4f20\u5165\u4e24\u4e2a\u53d8\u91cf\u3002\nint x = 9, y = 10;\nConsole.WriteLine(&quot;Before call: X: {0}, Y: {1}&quot;, x, y);\nConsole.WriteLine(&quot;Answer is: {0}&quot;, Add(x, y));\nConsole.WriteLine(&quot;After call: X: {0}, Y: {1}&quot;, x, y); \nConsole.ReadLine();\n\n\/\/ Value type arguments are passed by value by default.\n\/\/ \u9ed8\u8ba4\u60c5\u51b5\u4e0b\uff0c\u503c\u7c7b\u578b\u53c2\u6570\u6309\u503c\u4f20\u9012\u3002\nstatic int Add(int x, int y)\n{\n    int ans = x + y;\n    \/\/ Caller will not see these changes\n    \/\/ \u8c03\u7528\u8005\u770b\u4e0d\u5230\u8fd9\u4e9b\n    \/\/ as you are modifying a copy of the original data.\n    \/\/ \u4f60\u4fee\u6539\u7684\u539f\u59cb\u6570\u636e\u526f\u672c\n\n    x = 10000;\n    y = 88888;\n    return ans;\n}\n<\/pre>\n<\/p>\n<p>As you would hope, the values of x and y remain identical before and after the call to Add(), as shown in the following output, as the data points were sent in by value. Thus, any changes on these parameters within the Add() method are not seen by the caller, as the Add() method is operating on a copy of the data.<br \/>\n\u5982\u60a8\u6240\u613f\uff0c\u5728\u8c03\u7528Add\uff08\uff09\u4e4b\u524d\u548c\u4e4b\u540e\uff0cx\u548cy\u7684\u503c\u4fdd\u6301\u4e0d\u53d8\uff0c\u5982\u4ee5\u4e0b\u8f93\u51fa\u6240\u793a\uff0c\u56e0\u4e3a\u6570\u636e\u70b9\u662f\u6309\u503c\u53d1\u9001\u7684\u3002\u56e0\u6b64\uff0c\u8c03\u7528\u7a0b\u5e8f\u770b\u4e0d\u5230Add\uff08\uff09\u65b9\u6cd5\u4e2d\u8fd9\u4e9b\u53c2\u6570\u7684\u4efb\u4f55\u66f4\u6539\uff0c\u56e0\u4e3aAdd\uff08\uff09\u6cd5\u662f\u5728\u6570\u636e\u526f\u672c\u4e0a\u64cd\u4f5c\u7684\u3002<\/p>\n<p><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n***** Fun with Methods *****\n\nBefore call: X: 9, Y: 10\nAnswer is: 19\nAfter call: X: 9, Y: 10\n<\/pre>\n<\/p>\n<h2>The Default Behavior for Reference Types<\/h2>\n<p>\u5f15\u7528\u7c7b\u578b\u7684\u9ed8\u8ba4\u884c\u4e3a<\/p>\n<p>The default way a reference type parameter is sent into a function is by reference for its properties, but by value for itself. This is covered in detail later in this chapter, after the discussion of value types and reference types.<br \/>\n\u5c06\u5f15\u7528\u7c7b\u578b\u53c2\u6570\u53d1\u9001\u5230\u51fd\u6570\u7684\u9ed8\u8ba4\u65b9\u5f0f\u662f\u901a\u8fc7\u5bf9\u5176\u5c5e\u6027\u7684\u5f15\u7528\uff0c\u800c\u4e0d\u662f\u901a\u8fc7\u5bf9\u5176\u81ea\u8eab\u7684\u503c\u3002\u5728\u8ba8\u8bba\u4e86\u503c\u7c7b\u578b\u548c\u5f15\u7528\u7c7b\u578b\u4e4b\u540e\uff0c\u672c\u7ae0\u7a0d\u540e\u5c06\u5bf9\u6b64\u8fdb\u884c\u8be6\u7ec6\u4ecb\u7ecd\u3002<\/p>\n<blockquote><p>\n\u25a0 Note even though the string data type is technically a reference type, as discussed in Chapter 3, it\u2019s a special case. When a string parameter does not have a modifier, it is passed in by value.<br \/>\n\u8bf7\u6ce8\u610f\uff0c\u5c3d\u7ba1\u5b57\u7b26\u4e32\u6570\u636e\u7c7b\u578b\u5728\u6280\u672f\u4e0a\u662f\u4e00\u79cd\u5f15\u7528\u7c7b\u578b\uff0c\u5982\u7b2c3\u7ae0\u6240\u8ff0\uff0c\u4f46\u8fd9\u662f\u4e00\u79cd\u7279\u6b8a\u60c5\u51b5\u3002\u5f53\u5b57\u7b26\u4e32\u53c2\u6570\u6ca1\u6709\u4fee\u9970\u7b26\u65f6\uff0c\u5b83\u4f1a\u901a\u8fc7\u503c\u4f20\u5165\u3002\n<\/p><\/blockquote>\n<h2>Using the out Modifier (Updated 7.0)<\/h2>\n<p>\u4f7f\u7528 out \u4fee\u9970\u7b26\uff087.0 \u66f4\u65b0\uff09<\/p>\n<p>Next, you have the use of output parameters. Methods that have been defined to take output parameters (via the out keyword) are under obligation to assign them to an appropriate value before exiting the method scope (if you fail to do so, you will receive compiler errors).<br \/>\n\u63a5\u4e0b\u6765\uff0c\u60a8\u53ef\u4ee5\u4f7f\u7528\u8f93\u51fa\u53c2\u6570\u3002\u5df2\u5b9a\u4e49\u4e3a\u91c7\u7528\u8f93\u51fa\u53c2\u6570\uff08\u901a\u8fc7 out \u5173\u952e\u5b57\uff09\u7684\u65b9\u6cd5\u6709\u5728\u9000\u51fa\u65b9\u6cd5\u8303\u56f4\u4e4b\u524d\u5c06\u5b83\u4eec\u5206\u914d\u7ed9\u9002\u5f53\u7684\u503c\uff08\u5982\u679c\u4e0d\u8fd9\u6837\u505a\uff0c\u60a8\u5c06\u6536\u5230\u7f16\u8bd1\u5668\u9519\u8bef\uff09\u3002<\/p>\n<p>To illustrate, here is an alternative version of the Add() method that returns the sum of two integers using the C# out modifier (note the physical return value of this method is now void):<br \/>\n\u4e3a\u4e86\u8bf4\u660e\u8fd9\u4e00\u70b9\uff0c\u4e0b\u9762\u662fAdd\uff08\uff09\u65b9\u6cd5\u7684\u53e6\u4e00\u4e2a\u7248\u672c\uff0c\u5b83\u4f7f\u7528out\u4fee\u9970\u7b26\u8fd4\u56de\u4e24\u4e2a\u6574\u6570\u7684\u548c\uff08\u8bf7\u6ce8\u610f\uff0c\u8be5\u65b9\u6cd5\u7684\u8fd4\u56de\u503c\u73b0\u5728\u4e3avoid\uff09\uff1a<\/p>\n<p><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n\/\/ Output parameters must be assigned by the called method.\n\/\/ \u8f93\u51fa\u53c2\u6570\u5fc5\u987b\u7531\u8c03\u7528\u7684\u65b9\u6cd5\u5206\u914d\u3002\nstatic void AddUsingOutParam(int x, int y, out int ans)\n{\n    ans = x + y;\n}\n<\/pre>\n<\/p>\n<p>Calling a method with output parameters also requires the use of the out modifier. However, the local variables that are passed as output variables are not required to be assigned before passing them in as output arguments (if you do so, the original value is lost after the call). The reason the compiler allows you to send in seemingly unassigned data is because the method being called must make an assignment. To call the updated Add method, create a variable of type int, and use the out modifier in the call, like this:<br \/>\n\u4f7f\u7528\u5e26\u6709\u8f93\u51fa\u53c2\u6570\u7684\u65b9\u6cd5\u8fd8\u9700\u8981\u4f7f\u7528out\u4fee\u9970\u7b26\u3002\u4f46\u662f\uff0c\u4f5c\u4e3a\u8f93\u51fa\u53d8\u91cf\u4f20\u9012\u7684\u5c40\u90e8\u53d8\u91cf\u5728\u4f5c\u4e3a\u8f93\u51fa\u53c2\u6570\u4f20\u9012\u4e4b\u524d\u4e0d\u9700\u8981\u8d4b\u503c\uff08\u5982\u679c\u8fd9\u6837\u505a\uff0c\u5219\u539f\u59cb\u503c\u5728\u8c03\u7528\u540e\u4e22\u5931\uff09\u3002\u7f16\u8bd1\u5668\u5141\u8bb8\u60a8\u53d1\u9001\u770b\u4f3c\u672a\u5206\u914d\u7684\u6570\u636e\u7684\u539f\u56e0\u662f\uff0c\u88ab\u8c03\u7528\u7684\u65b9\u6cd5\u5fc5\u987b\u8fdb\u884c\u8d4b\u503c\u3002\u82e5\u8981\u8c03\u7528\u66f4\u65b0\u540e\u7684Add\u65b9\u6cd5\uff0c\u8bf7\u521b\u5efa\u4e00\u4e2aint\u7c7b\u578b\u7684\u53d8\u91cf\uff0c\u5e76\u5728\u8c03\u7528\u4e2d\u4f7f\u7528out\u4fee\u9970\u7b26\uff0c\u5982\u4e0b\u6240\u793a\uff1a<\/p>\n<p><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nint ans;\nAddUsingOutParam(90, 90, out ans);\n<\/pre>\n<\/p>\n<p>Starting with C# 7.0, out parameters do not need to be declared before using them. In other words, they can be declared inside the method call, like this:<br \/>\n\u4eceC#7.0\u5f00\u59cb\uff0cout\u53c2\u6570\u5728\u4f7f\u7528\u4e4b\u524d\u4e0d\u9700\u8981\u58f0\u660e\u3002\u6362\u53e5\u8bdd\u8bf4\uff0c\u5b83\u4eec\u53ef\u4ee5\u5728\u65b9\u6cd5\u8c03\u7528\u4e2d\u58f0\u660e\uff0c\u5982\u4e0b\u6240\u793a\uff1a<\/p>\n<p><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nAddUsingOutParam(90, 90, out int ans);\n<\/pre>\n<\/p>\n<p>The following code is an example of calling a method with an inline declaration of the out parameter:<br \/>\n\u4e0b\u9762\u7684\u4ee3\u7801\u662f\u4f7f\u7528 out \u53c2\u6570\u7684\u5185\u8054\u58f0\u660e\u8c03\u7528\u65b9\u6cd5\u7684\u793a\u4f8b\uff1a<\/p>\n<p><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nConsole.WriteLine(&quot;***** Fun with Methods *****&quot;);\n\/\/ No need to assign initial value to local variables\n\/\/ \u65e0\u9700\u4e3a\u5c40\u90e8\u53d8\u91cf\u8d4b\u503c\n\/\/ used as output parameters, provided the first time\n\/\/ \u7528\u4f5c\u8f93\u51fa\u53c2\u6570\uff0c\u9996\u6b21\u63d0\u4f9b\n\/\/ you use them is as output arguments.\n\/\/ \u5c06\u5b83\u4eec\u7528\u4f5c\u8f93\u51fa\u53c2\u6570\u3002\n\/\/ C# 7 allows for out parameters to be declared in the method call\n\/\/ C# 7 \u5141\u8bb8\u5728\u65b9\u6cd5\u8c03\u7528\nAddUsingOutParam(90, 90, out int ans);\nConsole.WriteLine(&quot;90 + 90 = {0}&quot;, ans);\nConsole.ReadLine();\n\n\/\/ Output parameters must be assigned by the called method.\n\/\/ \u8f93\u51fa\u53c2\u6570\u5fc5\u987b\u7531\u8c03\u7528\u7684\u65b9\u6cd5\u5206\u914d\u3002\nstatic void AddUsingOutParam(int x, int y, out int ans)\n{\n    ans = x + y;\n}\n<\/pre>\n<\/p>\n<p>The previous example is intended to be illustrative in nature; you really have no reason to return the value of your summation using an output parameter. However, the C# out modifier does serve a useful purpose: it allows the caller to obtain multiple outputs from a single method invocation.<br \/>\n\u524d\u9762\u7684\u4f8b\u5b50\u672c\u8d28\u4e0a\u662f\u4e3a\u4e86\u8bf4\u660e\u95ee\u9898\uff1b\u60a8\u786e\u5b9e\u6ca1\u6709\u7406\u7531\u4f7f\u7528\u8f93\u51fa\u53c2\u6570\u8fd4\u56de\u6c42\u548c\u7684\u503c\u3002\u7136\u800c\uff0cout\u4fee\u9970\u7b26\u786e\u5b9e\u6709\u4e00\u4e2a\u6709\u7528\u7684\u7528\u9014\uff1a\u5b83\u5141\u8bb8\u8c03\u7528\u8005\u4ece\u5355\u4e2a\u65b9\u6cd5\u8c03\u7528\u4e2d\u83b7\u5f97\u591a\u4e2a\u8f93\u51fa\u3002<\/p>\n<p><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n\/\/ Returning multiple output parameters.\n\/\/ \/\/ \u8fd4\u56de\u591a\u4e2a\u8f93\u51fa\u53c2\u6570\u3002\nstatic void FillTheseValues(out int , out string b, out bool c)\n{\n    a = 9;\n    b = &quot;Enjoy your string.&quot;;\n    c = true;\n}\n<\/pre>\n<\/p>\n<p>The caller would be able to invoke the FillTheseValues() method. Remember that you must use the out modifier when you invoke the method, as well as when you implement the method.<br \/>\n\u8c03\u7528\u65b9\u5c06\u80fd\u591f\u8c03\u7528FillTheseValue\uff08\uff09\u65b9\u6cd5\u3002\u8bf7\u8bb0\u4f4f\uff0c\u5728\u8c03\u7528\u8be5\u65b9\u6cd5\u65f6\u4ee5\u53ca\u5b9e\u73b0\u8be5\u65b9\u6cd5\u65f6\uff0c\u5fc5\u987b\u4f7f\u7528out\u4fee\u9970\u7b26\u3002<\/p>\n<p><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nConsole.WriteLine(&quot;***** Fun with Methods *****&quot;);\nFillTheseValues(out int i, out string str, out bool b);\nConsole.WriteLine(&quot;Int is: {0}&quot;, i);\nConsole.WriteLine(&quot;String is: {0}&quot;, str);\nConsole.WriteLine(&quot;Boolean is: {0}&quot;, b);\nConsole.ReadLine();\n\n\n\/\/ Returning multiple output parameters.\n\/\/ \/\/ \u8fd4\u56de\u591a\u4e2a\u8f93\u51fa\u53c2\u6570\u3002\nstatic void FillTheseValues(out int a, out string b, out bool c)\n{\n    a = 9;\n    b = &quot;Enjoy your string.&quot;;\n    c = true;\n}\n<\/pre>\n<\/p>\n<blockquote><p>\n\u25a0Note C# 7 also introduced tuples, which are another way to return multiple values out of a method call. You will learn more about that later in this chapter.<br \/>\n\u6ce8\u610f C# 7 \u8fd8\u5f15\u5165\u4e86\u5143\u7ec4\uff0c\u5143\u7ec4\u662f\u4ece\u65b9\u6cd5\u8c03\u7528\u4e2d\u8fd4\u56de\u591a\u4e2a\u503c\u7684\u53e6\u4e00\u79cd\u65b9\u6cd5\u3002\u60a8\u5c06\u5728\u672c\u7ae0\u540e\u9762\u5bf9\u6b64\u8fdb\u884c\u8be6\u7ec6\u4ecb\u7ecd\u3002\n<\/p><\/blockquote>\n<p>Always remember that a method that defines output parameters must assign the parameter to a valid value before exiting the method scope. Therefore, the following code will result in a compiler error, as the output parameter has not been assigned within the method scope:<br \/>\n\u8bf7\u59cb\u7ec8\u8bb0\u4f4f\uff0c\u5b9a\u4e49\u8f93\u51fa\u53c2\u6570\u7684\u65b9\u6cd5\u5fc5\u987b\u5728\u9000\u51fa\u65b9\u6cd5\u8303\u56f4\u4e4b\u524d\u5c06\u53c2\u6570\u5206\u914d\u7ed9\u6709\u6548\u503c\u3002\u56e0\u6b64\uff0c\u4ee5\u4e0b\u4ee3\u7801\u5c06\u5bfc\u81f4\u7f16\u8bd1\u5668\u9519\u8bef\uff0c\u56e0\u4e3a\u5c1a\u672a\u5728\u65b9\u6cd5\u8303\u56f4\u5185\u5206\u914d\u8f93\u51fa\u53c2\u6570\uff1a<\/p>\n<p><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nstatic void ThisWontCompile(out int a)\n{\n    Console.WriteLine(&quot;Error! Forgot to assign output arg!&quot;);\n}\n<\/pre>\n<\/p>\n<h2>Discarding out Parameters (New 7.0)<\/h2>\n<p>\u4e22\u5f03\u53c2\u6570\uff08\u65b0 7.0\uff09<\/p>\n<p>If you do not care about the value of an out parameter, you can use a discard as a placeholder. Discards are temporary, dummy variables that are intentionally unused. They are unassigned, do not have a value, and might not even allocate any memory. This can provide a performance benefit as well as make your code more readable. Discards can be used with out parameters, with tuples (later in this chapter), with pattern matching (Chapters 6 and 8), or even as stand-alone variables.<br \/>\n\u5982\u679c\u4e0d\u5173\u5fc3out\u53c2\u6570\u7684\u503c\uff0c\u53ef\u4ee5\u4f7f\u7528discard\u4f5c\u4e3a\u5360\u4f4d\u7b26\u3002\u4e22\u5f03\u662f\u6545\u610f\u672a\u4f7f\u7528\u7684\u4e34\u65f6\u4f2a\u53d8\u91cf\u3002\u5b83\u4eec\u662f\u672a\u5206\u914d\u7684\uff0c\u6ca1\u6709\u503c\uff0c\u751a\u81f3\u53ef\u80fd\u4e0d\u4f1a\u5206\u914d\u4efb\u4f55\u5185\u5b58\u3002\u8fd9\u53ef\u4ee5\u63d0\u4f9b\u6027\u80fd\u4f18\u52bf\uff0c\u5e76\u4f7f\u4ee3\u7801\u66f4\u5177\u53ef\u8bfb\u6027\u3002Discards\u53ef\u4ee5\u4e0eout\u53c2\u6570\u3001\u5143\u7ec4\uff08\u672c\u7ae0\u7a0d\u540e\u90e8\u5206\uff09\u3001\u6a21\u5f0f\u5339\u914d\uff08\u7b2c6\u7ae0\u548c\u7b2c8\u7ae0\uff09\u4e00\u8d77\u4f7f\u7528\uff0c\u751a\u81f3\u53ef\u4ee5\u4f5c\u4e3a\u72ec\u7acb\u53d8\u91cf\u4f7f\u7528\u3002<\/p>\n<p>For example, if you want to get the value for the int in the previous example but do not care about the second two parameters, you can write the following code:<br \/>\n\u4f8b\u5982\uff0c\u5982\u679c\u60a8\u60f3\u83b7\u5f97\u4e0a\u4e00\u4e2a\u793a\u4f8b\u4e2dint\u7684\u503c\uff0c\u4f46\u4e0d\u5173\u5fc3\u540e\u4e24\u4e2a\u53c2\u6570\uff0c\u5219\u53ef\u4ee5\u7f16\u5199\u4ee5\u4e0b\u4ee3\u7801\uff1a<\/p>\n<p><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nConsole.WriteLine(&quot;***** Fun with Methods *****&quot;);\n\n\/\/This only gets the value for a, and ignores the other two parameters\n\/\/  \u8fd9\u53ea\u83b7\u53d6 a \u7684\u503c\uff0c\u5e76\u5ffd\u7565\u5176\u4ed6\u4e24\u4e2a\u53c2\u6570\nFillTheseValues(out int a, out _, out _);\nConsole.WriteLine(&quot;Int is: {0}&quot;, a);\n\nConsole.ReadLine();\n\n\/\/ Returning multiple output parameters.\n\/\/ \/\/ \u8fd4\u56de\u591a\u4e2a\u8f93\u51fa\u53c2\u6570\u3002\nstatic void FillTheseValues(out int a, out string b, out bool c)\n{\n    a = 9;\n    b = &quot;Enjoy your string.&quot;;\n    c = true;\n}\n<\/pre>\n<\/p>\n<p>Note that the called method is still doing the work setting the values for all three parameters; it is just that the last two parameters are being discarded when the method call returns.<br \/>\n\u8bf7\u6ce8\u610f\uff0c\u88ab\u8c03\u7528\u7684\u65b9\u6cd5\u4ecd\u5728\u4e3a\u6240\u6709\u4e09\u4e2a\u53c2\u6570\u8bbe\u7f6e\u503c\uff1b\u53ea\u662f\u5f53\u65b9\u6cd5\u8c03\u7528\u8fd4\u56de\u65f6\uff0c\u6700\u540e\u4e24\u4e2a\u53c2\u6570\u5c06\u88ab\u4e22\u5f03\u3002<\/p>\n<h2>The out Modifier in Constructors and Initializers (New 7.3)<\/h2>\n<p>\u6784\u9020\u51fd\u6570\u548c\u521d\u59cb\u503c\u8bbe\u5b9a\u9879\u4e2d\u7684 out \u4fee\u9970\u7b26\uff08\u65b0 7.3\uff09<\/p>\n<p>C# 7.3 extended the allowable locations for using the out parameter. In addition to methods, parameters for constructors, field and property initializers, and query clauses can all be decorated with the out modifier. Examples of these will be examined later in this book.<br \/>\nC#7.3\u6269\u5c55\u4e86\u4f7f\u7528out\u53c2\u6570\u7684\u5141\u8bb8\u4f4d\u7f6e\u3002\u9664\u4e86\u65b9\u6cd5\u4e4b\u5916\uff0c\u6784\u9020\u51fd\u6570\u7684\u53c2\u6570\u3001\u5b57\u6bb5\u548c\u5c5e\u6027\u521d\u59cb\u503c\u8bbe\u5b9a\u9879\u4ee5\u53ca\u67e5\u8be2\u5b50\u53e5\u90fd\u53ef\u4ee5\u7528out\u4fee\u9970\u7b26\u8fdb\u884c\u4fee\u9970\u3002\u8fd9\u4e9b\u4f8b\u5b50\u5c06\u5728\u672c\u4e66\u540e\u9762\u8fdb\u884c\u7814\u7a76\u3002<\/p>\n<h2>Using the ref Modifier<\/h2>\n<p>\u4f7f\u7528 ref \u4fee\u9970\u7b26<\/p>\n<p>Now consider the use of the C# ref parameter modifier. Reference parameters are necessary when you want to allow a method to operate on (and usually change the values of) various data points declared in the caller\u2019s scope (such as a sorting or swapping routine). Note the distinction between output and reference parameters:<br \/>\n\u73b0\u5728\u8003\u8651\u4f7f\u7528ref\u53c2\u6570\u4fee\u9970\u7b26\u3002\u5f53\u60a8\u5e0c\u671b\u5141\u8bb8\u65b9\u6cd5\u5bf9\u8c03\u7528\u65b9\u4f5c\u7528\u57df\u4e2d\u58f0\u660e\u7684\u5404\u79cd\u6570\u636e\u70b9\uff08\u5982\u6392\u5e8f\u6216\u4ea4\u6362\u4f8b\u7a0b\uff09\u8fdb\u884c\u64cd\u4f5c\uff08\u901a\u5e38\u66f4\u6539\u5176\u503c\uff09\u65f6\uff0c\u5f15\u7528\u53c2\u6570\u662f\u5fc5\u8981\u7684\u3002\u6ce8\u610f\u8f93\u51fa\u53c2\u6570\u548c\u5f15\u7528\u53c2\u6570\u4e4b\u95f4\u7684\u533a\u522b\uff1a<\/p>\n<ul>\n<li>Output parameters do not need to be initialized before they are passed to the method. The reason for this is that the method must assign output parameters before exiting.<br \/>\n\u8f93\u51fa\u53c2\u6570\u5728\u4f20\u9012\u7ed9\u65b9\u6cd5\u4e4b\u524d\u4e0d\u9700\u8981\u521d\u59cb\u5316\u3002\u539f\u56e0\u662f\u8be5\u65b9\u6cd5\u5fc5\u987b\u5728\u9000\u51fa\u4e4b\u524d\u5206\u914d\u8f93\u51fa\u53c2\u6570\u3002<\/li>\n<li>Reference parameters must be initialized before they are passed to the method. The reason for this is that you are passing a reference to an existing variable.<br \/>\n\u5f15\u7528\u53c2\u6570\u5fc5\u987b\u5148\u521d\u59cb\u5316\uff0c\u7136\u540e\u624d\u80fd\u4f20\u9012\u7ed9\u65b9\u6cd5\u3002\u8fd9\u6837\u505a\u7684\u539f\u56e0\u662f\u60a8\u6b63\u5728\u4f20\u9012\u5bf9\u73b0\u6709\u53d8\u91cf\u7684\u5f15\u7528\u3002<\/li>\n<\/ul>\n<p>If you do not assign it to an initial value, that would be the equivalent of operating on an unassigned local variable.<br \/>\n\u5982\u679c\u4e0d\u5c06\u5176\u5206\u914d\u7ed9\u521d\u59cb\u503c\uff0c\u5219\u7b49\u6548\u4e8e\u5bf9\u672a\u8d4b\u503c\u7684\u5c40\u90e8\u53d8\u91cf\u8fdb\u884c\u64cd\u4f5c\u3002<\/p>\n<p>Let\u2019s check out the use of the ref keyword by way of a method that swaps two string variables (of course, any two data types could be used here, including int, bool, float, etc.).<br \/>\n\u8ba9\u6211\u4eec\u901a\u8fc7\u4ea4\u6362\u4e24\u4e2a\u5b57\u7b26\u4e32\u53d8\u91cf\u7684\u65b9\u6cd5\u68c0\u67e5\u4e00\u4e0bref \u5173\u952e\u5b57\u7684\u4f7f\u7528\uff08\u5f53\u7136\uff0c\u8fd9\u91cc\u53ef\u4ee5\u4f7f\u7528\u4efb\u4f55\u4e24\u79cd\u6570\u636e\u7c7b\u578b\uff0c\u5305\u62ec int\u3001bool\u3001float \u7b49\uff09\u3002<\/p>\n<p><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n\/\/ Reference parameters.\n\/\/ \u5f15\u7528\u53c2\u6570\u3002\npublic static void SwapStrings(ref string s1, ref string s2)\n{\n    string tempStr = s1;\n    s1 = s2;\n    s2 = tempStr;\n}\n\n\/\/ NET7\u63a7\u5236\u53f0\u9876\u7ea7\u8bed\u53e5\u6a21\u677f\u4e2d\u63d0\u793apublic\u65e0\u6548\uff0c\u53bb\u6389\u5373\u53ef\u3002\n<\/pre>\n<\/p>\n<p>This method can be called as follows:<br \/>\n\u53ef\u4ee5\u6309\u5982\u4e0b\u65b9\u5f0f\u8c03\u7528\u6b64\u65b9\u6cd5\uff1a<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nConsole.WriteLine(&quot;***** Fun with Methods *****&quot;);\nstring str1 = &quot;Flip&quot;;\nstring str2 = &quot;Flop&quot;;\nConsole.WriteLine(&quot;Before: {0}, {1} &quot;, str1, str2);\nSwapStrings(ref str1, ref str2);\nConsole.WriteLine(&quot;After: {0}, {1} &quot;, str1, str2);\nConsole.ReadLine();\n\n\n\/\/ Reference parameters.\n\/\/ \u5f15\u7528\u53c2\u6570\u3002\nstatic void SwapStrings(ref string s1, ref string s2)\n{\n    string tempStr = s1;\n    s1 = s2;\n    s2 = tempStr;\n}\n<\/pre>\n<\/p>\n<p>Here, the caller has assigned an initial value to local string data (str1 and str2). After the call to SwapStrings() returns, str1 now contains the value &quot;Flop&quot;, while str2 reports the value &quot;Flip&quot;.<br \/>\n\u5728\u8fd9\u91cc\uff0c\u8c03\u7528\u65b9\u4e3a\u672c\u5730\u5b57\u7b26\u4e32\u6570\u636e\uff08str1 \u548c str2\uff09\u5206\u914d\u4e86\u4e00\u4e2a\u521d\u59cb\u503c\u3002\u8c03\u7528SwapStrings()\u8fd4\u56de\uff0cstr1 \u73b0\u5728\u662f \u201cFlop\u201d\uff0c\u800c str2 \u662f \u201cFlip\u201d\u3002<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n***** Fun with Methods *****\nBefore: Flip, Flop\nAfter: Flop, Flip\n<\/pre>\n<\/p>\n<h2>Using the in Modifier (New 7.2)<\/h2>\n<p>\u4f7f\u7528 in \u4fee\u9970\u7b26\uff08\u65b0\u7248 7.2\uff09<\/p>\n<p>The in modifier passes a value by reference (for both value and reference types) and prevents the called method from modifying the values. This clearly states a design intent in your code, as well as potentially reducing memory pressure. When value types are passed by value, they are copied (internally) by the called method. If the object is large (such as a large struct), the extra overhead of making a copy for local use can be significant. Also, even when reference types are passed without a modifier, they can be modified by the called method. Both issues can be resolved using the in modifier.<br \/>\nin\u4fee\u9970\u7b26\u901a\u8fc7\u5f15\u7528\u4f20\u9012\u503c\uff08\u5bf9\u4e8e\u503c\u548c\u5f15\u7528\u7c7b\u578b\uff09\uff0c\u5e76\u963b\u6b62\u88ab\u8c03\u7528\u7684\u65b9\u6cd5\u4fee\u6539\u503c\u3002\u8fd9\u6e05\u695a\u5730\u8bf4\u660e\u4e86\u4ee3\u7801\u4e2d\u7684\u8bbe\u8ba1\u610f\u56fe\uff0c\u5e76\u53ef\u80fd\u964d\u4f4e\u5185\u5b58\u538b\u529b\u3002\u5f53\u503c\u7c7b\u578b\u901a\u8fc7\u503c\u4f20\u9012\u65f6\uff0c\u5b83\u4eec\u4f1a\u88ab\u8c03\u7528\u7684\u65b9\u6cd5\uff08\u5185\u90e8\uff09\u590d\u5236\u3002\u5982\u679c\u5bf9\u8c61\u5f88\u5927\uff08\u6bd4\u5982\u4e00\u4e2a\u5927\u7ed3\u6784\uff09\uff0c\u90a3\u4e48\u4e3a\u672c\u5730\u4f7f\u7528\u5236\u4f5c\u526f\u672c\u7684\u989d\u5916\u5f00\u9500\u53ef\u80fd\u4f1a\u5f88\u5927\u3002\u6b64\u5916\uff0c\u5373\u4f7f\u5728\u4f20\u9012\u5f15\u7528\u7c7b\u578b\u65f6\u4e0d\u5e26\u4fee\u9970\u7b26\uff0c\u5b83\u4eec\u4e5f\u53ef\u4ee5\u901a\u8fc7\u8c03\u7528\u7684\u65b9\u6cd5\u8fdb\u884c\u4fee\u6539\u3002\u8fd9\u4e24\u4e2a\u95ee\u9898\u90fd\u53ef\u4ee5\u4f7f\u7528in\u4fee\u9970\u7b26\u6765\u89e3\u51b3\u3002<\/p>\n<p>Revisiting the Add() method from earlier, there are two lines of code that modify the parameters, but do not affect the values for the calling method. The values are not affected because the Add() method makes a copy of the variables x and y to use locally. While the calling method does not have any adverse side effects, what if the Add() method was changed to the following code?<br \/>\n\u91cd\u65b0\u8bbf\u95ee\u524d\u9762\u7684Add\uff08\uff09\u65b9\u6cd5\uff0c\u6709\u4e24\u884c\u4ee3\u7801\u53ef\u4ee5\u4fee\u6539\u53c2\u6570\uff0c\u4f46\u4e0d\u4f1a\u5f71\u54cd\u8c03\u7528\u65b9\u6cd5\u7684\u503c\u3002\u8fd9\u4e9b\u503c\u4e0d\u53d7\u5f71\u54cd\uff0c\u56e0\u4e3aAdd\uff08\uff09\u65b9\u6cd5\u751f\u6210\u53d8\u91cfx\u548cy\u7684\u526f\u672c\u4ee5\u4f9b\u672c\u5730\u4f7f\u7528\u3002\u867d\u7136\u8c03\u7528\u65b9\u6cd5\u6ca1\u6709\u4efb\u4f55\u4e0d\u5229\u7684\u526f\u4f5c\u7528\uff0c\u4f46\u5982\u679cAdd\uff08\uff09\u65b9\u6cd5\u88ab\u66f4\u6539\u4e3a\u4ee5\u4e0b\u4ee3\u7801\u5462\uff1f<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nstatic int Add2(int x, int y)\n{\n    x = 10000;\n    y = 88888;\n    int ans = x + y;\n    return ans;\n}\n<\/pre>\n<\/p>\n<p>Running this code then returns 98888, regardless of the numbers sent into the method. This is obviously a problem. To correct this, update the method to the following:<br \/>\n\u8fd0\u884c\u6b64\u4ee3\u7801\u5c06\u8fd4\u56de98888\uff0c\u800c\u4e0e\u53d1\u9001\u5230\u65b9\u6cd5\u4e2d\u7684\u6570\u5b57\u65e0\u5173\u3002\u8fd9\u663e\u7136\u662f\u4e2a\u95ee\u9898\u3002\u8981\u66f4\u6b63\u6b64\u95ee\u9898\uff0c\u8bf7\u5c06\u65b9\u6cd5\u66f4\u65b0\u4e3a\u4ee5\u4e0b\u5185\u5bb9\uff1a<\/p>\n<p><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nstatic int AddReadOnly(in int x, in int y)\n{\n    \/\/Error CS8331 Cannot assign to variable 'in int' because it is a readonly variable\n    \/\/x = 10000;\n    \/\/y = 88888;\n    int ans = x + y;\n    return ans;\n}\n<\/pre>\n<\/p>\n<p>When the code attempts to change the values of the parameters, the compiler raises the CS8331 error, indicating that the values cannot be modified because of the in modifier.<br \/>\n\u5f53\u4ee3\u7801\u5c1d\u8bd5\u66f4\u6539\u53c2\u6570\u7684\u503c\u65f6\uff0c\u7f16\u8bd1\u5668\u4f1a\u5f15\u53d1 CS8331 \u9519\u8bef\uff0c\u6307\u793a\u7531\u4e8e in \u4fee\u9970\u7b26\u800c\u65e0\u6cd5\u4fee\u6539\u8fd9\u4e9b\u503c\u3002<\/p>\n<h2>Using the params Modifier<\/h2>\n<p>\u4f7f\u7528\u53c2\u6570\u4fee\u9970\u7b26<\/p>\n<p>C# supports the use of parameter arrays using the params keyword. The params keyword allows you to pass into a method a variable number of identically typed parameters (or classes related by inheritance) as a single logical parameter. As well, arguments marked with the params keyword can be processed if the caller sends in a strongly typed array or a comma-delimited list of items. Yes, this can be confusing! To clear things up, assume you want to create a function that allows the caller to pass in any number of arguments and return the calculated average.<br \/>\nC#\u652f\u6301\u4f7f\u7528params\u5173\u952e\u5b57\u7684\u53c2\u6570\u6570\u7ec4\u3002params\u5173\u952e\u5b57\u5141\u8bb8\u60a8\u5c06\u6570\u91cf\u53ef\u53d8\u7684\u540c\u7c7b\u578b\u53c2\u6570\uff08\u6216\u901a\u8fc7\u7ee7\u627f\u76f8\u5173\u7684\u7c7b\uff09\u4f5c\u4e3a\u5355\u4e2a\u903b\u8f91\u53c2\u6570\u4f20\u9012\u5230\u65b9\u6cd5\u4e2d\u3002\u6b64\u5916\uff0c\u5982\u679c\u8c03\u7528\u65b9\u53d1\u9001\u5f3a\u7c7b\u578b\u6570\u7ec4\u6216\u9017\u53f7\u5206\u9694\u7684\u9879\u76ee\u5217\u8868\uff0c\u5219\u53ef\u4ee5\u5904\u7406\u7528params\u5173\u952e\u5b57\u6807\u8bb0\u7684\u53c2\u6570\u3002\u662f\u7684\uff0c\u8fd9\u53ef\u80fd\u4f1a\u8ba9\u4eba\u56f0\u60d1\uff01\u4e3a\u4e86\u89e3\u51b3\u95ee\u9898\uff0c\u5047\u8bbe\u60a8\u60f3\u8981\u521b\u5efa\u4e00\u4e2a\u51fd\u6570\uff0c\u5141\u8bb8\u8c03\u7528\u65b9\u4f20\u5165\u4efb\u610f\u6570\u91cf\u7684\u53c2\u6570\u5e76\u8fd4\u56de\u8ba1\u7b97\u51fa\u7684\u5e73\u5747\u503c\u3002<\/p>\n<p>If you were to prototype this method to take an array of doubles, this would force the caller to first define the array, then fill the array, and finally pass it into the method. However, if you define CalculateAverage() to take a params of double[] data types, the caller can simply pass a comma-delimited list of doubles. The list of doubles will be packaged into an array of doubles behind the scenes.<br \/>\n\u5982\u679c\u60a8\u8981\u5c06\u6b64\u65b9\u6cd5\u539f\u578b\u8bbe\u8ba1\u4e3a\u4e00\u4e2adouble\u6570\u7ec4\uff0c\u8fd9\u5c06\u5f3a\u5236\u8c03\u7528\u65b9\u9996\u5148\u5b9a\u4e49\u6570\u7ec4\uff0c\u7136\u540e\u586b\u5145\u6570\u7ec4\uff0c\u6700\u540e\u5c06\u5176\u4f20\u9012\u5230\u65b9\u6cd5\u4e2d\u3002\u4f46\u662f\uff0c\u5982\u679c\u60a8\u5c06CalculateAverage()\u5b9a\u4e49\u4e3a\u91c7\u7528double[]\u6570\u636e\u7c7b\u578b\u7684\u53c2\u6570\uff0c\u5219\u8c03\u7528\u8005\u53ef\u4ee5\u7b80\u5355\u5730\u4f20\u9012\u4e00\u4e2a\u9017\u53f7\u5206\u9694\u7684doubles\u5217\u8868\u3002\u66ff\u8eab\u5217\u8868\u5c06\u88ab\u6253\u5305\u4e3a\u4e00\u7ec4\u5e55\u540e\u66ff\u8eab\u3002<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n\/\/ Return average of &quot;some number&quot; of doubles.\n\/\/ \u8fd4\u56de\u201c\u4e00\u4e9b\u6570\u91cf\u201d\u7684\u53cc\u7cbe\u5ea6\u7684\u5e73\u5747\u503c\u3002\nstatic double CalculateAverage(params double&#x5B;] values)\n{\n    Console.WriteLine(&quot;You sent me {0} doubles.&quot;, values.Length);\n    double sum = 0;\n    if (values.Length == 0)\n    {\n        return sum;\n    }\n    for (int i = 0; i &lt; values.Length; i++)\n    {\n        sum += values&#x5B;i];\n    }\n    return (sum \/ values.Length);\n}\n<\/pre>\n<\/p>\n<p>This method has been defined to take a parameter array of doubles. What this method is in fact saying is \u201cSend me any number of doubles (including zero), and I\u2019ll compute the average.\u201d Given this, you can call CalculateAverage() in any of the following ways:<br \/>\n\u6b64\u65b9\u6cd5\u5df2\u88ab\u5b9a\u4e49\u4e3a\u63a5\u53d7\u4e00\u4e2adoubles\u6570\u7ec4\u3002\u4e8b\u5b9e\u4e0a\uff0c\u8fd9\u4e2a\u65b9\u6cd5\u7684\u610f\u601d\u662f\u201c\u7ed9\u6211\u53d1\u9001\u4efb\u610f\u6570\u91cf\u7684doubles\uff08\u5305\u62ec\u96f6\uff09\uff0c\u6211\u4f1a\u8ba1\u7b97\u5e73\u5747\u503c\u3002\u201d\u8003\u8651\u5230\u8fd9\u4e00\u70b9\uff0c\u4f60\u53ef\u4ee5\u7528\u4ee5\u4e0b\u4efb\u4f55\u4e00\u79cd\u65b9\u5f0f\u8c03\u7528CalculateAverage()\uff1a<\/p>\n<p><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nConsole.WriteLine(&quot;***** Fun with Methods *****&quot;);\n\/\/ Pass in a comma-delimited list of doubles...\n\/\/ \u4f20\u5165\u4ee5\u9017\u53f7\u5206\u9694\u7684doubles\u5217\u8868...\ndouble average;\naverage = CalculateAverage(4.0, 3.2, 5.7, 64.22, 87.2);\nConsole.WriteLine(&quot;Average of data is: {0}&quot;, average);\n\n\/\/ ...or pass an array of doubles.\n\/\/ \u6216\u8005\u4f20\u5165doubles\u6570\u7ec4\ndouble&#x5B;] data = { 4.0, 3.2, 5.7 };\naverage = CalculateAverage(data);\nConsole.WriteLine(&quot;Average of data is: {0}&quot;, average);\n\n\/\/ Average of 0 is 0!\nConsole.WriteLine(&quot;Average of data is: {0}&quot;, CalculateAverage());\nConsole.ReadLine();\n\n\/\/ Return average of &quot;some number&quot; of doubles.\n\/\/ \u8fd4\u56de\u201c\u4e00\u4e9b\u6570\u91cf\u201d\u7684\u53cc\u7cbe\u5ea6\u7684\u5e73\u5747\u503c\u3002\nstatic double CalculateAverage(params double&#x5B;] values)\n{\n    Console.WriteLine(&quot;You sent me {0} doubles.&quot;, values.Length);\n    double sum = 0;\n    if (values.Length == 0)\n    {\n        return sum;\n    }\n    for (int i = 0; i &lt; values.Length; i++)\n    {\n        sum += values&#x5B;i];\n    }\n    return (sum \/ values.Length);\n}\n<\/pre>\n<\/p>\n<p>If you did not make use of the params modifier in the definition of CalculateAverage(), the first invocation of this method would result in a compiler error, as the compiler would be looking for a version of CalculateAverage() that took five double arguments.<br \/>\n\u5982\u679c\u5728CalculateAverage\uff08\uff09\u7684\u5b9a\u4e49\u4e2d\u6ca1\u6709\u4f7f\u7528params\u4fee\u9970\u7b26\uff0c\u5219\u6b64\u65b9\u6cd5\u7684\u7b2c\u4e00\u6b21\u8c03\u7528\u5c06\u5bfc\u81f4\u7f16\u8bd1\u5668\u9519\u8bef\uff0c\u56e0\u4e3a\u7f16\u8bd1\u5668\u5c06\u67e5\u627e\u5305\u542b\u4e94\u4e2adoubles\u7684CalculateEverage() \u7248\u672c\u3002<\/p>\n<blockquote><p>\n\u25a0Note to avoid any ambiguity, C# demands a method support only a single params argument, which must be the final argument in the parameter list.<br \/>\n\u8bf7\u6ce8\u610f\uff0c\u4e3a\u4e86\u907f\u514d\u4efb\u4f55\u6b67\u4e49\uff0cC#\u8981\u6c42\u65b9\u6cd5\u53ea\u652f\u6301\u4e00\u4e2aparams\u53c2\u6570\uff0c\u8be5\u53c2\u6570\u5fc5\u987b\u662f\u53c2\u6570\u5217\u8868\u4e2d\u7684\u6700\u540e\u4e00\u4e2a\u53c2\u6570\u3002\n<\/p><\/blockquote>\n<p>As you might guess, this technique is nothing more than a convenience for the caller, given that the array is created by the .NET Core Runtime as necessary. By the time the array is within the scope of the method being called, you can treat it as a full-blown .NET Core array that contains all the functionality of the System.Array base class library type. Consider the following output:<br \/>\n\u6b63\u5982\u60a8\u53ef\u80fd\u731c\u6d4b\u7684\u90a3\u6837\uff0c\u8003\u8651\u5230\u6570\u7ec4\u662f\u7531.NET Core Runtime\u6839\u636e\u9700\u8981\u521b\u5efa\u7684\uff0c\u8fd9\u79cd\u6280\u672f\u53ea\u4e0d\u8fc7\u662f\u4e3a\u8c03\u7528\u7a0b\u5e8f\u63d0\u4f9b\u4e86\u4fbf\u5229\u3002\u5f53\u6570\u7ec4\u4f4d\u4e8e\u88ab\u8c03\u7528\u65b9\u6cd5\u7684\u8303\u56f4\u5185\u65f6\uff0c\u60a8\u53ef\u4ee5\u5c06\u5176\u89c6\u4e3a\u4e00\u4e2a\u5b8c\u6574\u7684.NET Core\u6570\u7ec4\uff0c\u5b83\u5305\u542bSystem.array\u57fa\u7c7b\u5e93\u7c7b\u578b\u7684\u6240\u6709\u529f\u80fd\u3002\u8003\u8651\u4ee5\u4e0b\u8f93\u51fa\uff1a<\/p>\n<p><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n***** Fun with Methods *****\nYou sent me 5 doubles.\nAverage of data is: 32.864\nYou sent me 3 doubles.\nAverage of data is: 4.3\nYou sent me 0 doubles.\nAverage of data is: 0\n<\/pre>\n<\/p>\n<h2>Defining Optional Parameters<\/h2>\n<p>\u5b9a\u4e49\u53ef\u9009\u53c2\u6570<\/p>\n<p>C# allows you to create methods that can take optional arguments. This technique allows the caller to invoke a single method while omitting arguments deemed unnecessary, provided the caller is happy with the specified defaults.<br \/>\nC#\u5141\u8bb8\u60a8\u521b\u5efa\u53ef\u4ee5\u91c7\u7528\u53ef\u9009\u53c2\u6570\u7684\u65b9\u6cd5\u3002\u8fd9\u79cd\u6280\u672f\u5141\u8bb8\u8c03\u7528\u65b9\u8c03\u7528\u5355\u4e2a\u65b9\u6cd5\uff0c\u540c\u65f6\u7701\u7565\u88ab\u8ba4\u4e3a\u4e0d\u5fc5\u8981\u7684\u53c2\u6570\uff0c\u524d\u63d0\u662f\u8c03\u7528\u65b9\u5bf9\u6307\u5b9a\u7684\u9ed8\u8ba4\u503c\u6ee1\u610f\u3002<\/p>\n<p>To illustrate working with optional arguments, assume you have a method named EnterLogData(), which defines a single optional parameter.<br \/>\n\u4e3a\u4e86\u8bf4\u660e\u5982\u4f55\u4f7f\u7528\u53ef\u9009\u53c2\u6570\uff0c\u5047\u8bbe\u60a8\u6709\u4e00\u4e2a\u540d\u4e3aEnterLogData()\u7684\u65b9\u6cd5\uff0c\u8be5\u65b9\u6cd5\u5b9a\u4e49\u4e86\u5355\u4e2a\u53ef\u9009\u53c2\u6570\u3002<\/p>\n<p><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nstatic void EnterLogData(string message, string owner = &quot;Programmer&quot;)\n{\n    Console.WriteLine(&quot;Error: {0}&quot;, message);\n    Console.WriteLine(&quot;Owner of Error: {0}&quot;, owner);\n}\n<\/pre>\n<\/p>\n<p>Here, the final string argument has been assigned the default value of &quot;Programmer&quot; via an assignment within the parameter definition. Given this, you can call EnterLogData() in two ways.<br \/>\n\u5728\u8fd9\u91cc\uff0c\u6700\u7ec8\u7684\u5b57\u7b26\u4e32\u53c2\u6570\u5df2\u901a\u8fc7\u53c2\u6570\u5b9a\u4e49\u4e2d\u7684\u8d4b\u503c\u5206\u914d\u4e86\u9ed8\u8ba4\u503c\u201cProgrammer\u201d\u3002 \u9274\u4e8e\u6b64\uff0c\u60a8\u53ef\u4ee5\u901a\u8fc7\u4e24\u79cd\u65b9\u5f0f\u8c03\u7528 EnterLogData\uff08\uff09\u3002<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nConsole.WriteLine(&quot;***** Fun with Methods *****&quot;);\n\nEnterLogData(&quot;Oh no! Grid can't find data&quot;);\nEnterLogData(&quot;Oh no! I can't find the payroll data&quot;, &quot;CFO&quot;);\nConsole.ReadLine();\n\nstatic void EnterLogData(string message, string owner = &quot;Programmer&quot;)\n{\n    Console.WriteLine(&quot;Error: {0}&quot;, message);\n    Console.WriteLine(&quot;Owner of Error: {0}&quot;, owner);\n}\n<\/pre>\n<\/p>\n<p>Because the first invocation of EnterLogData() did not specify a second string argument, you would find that the programmer is the one responsible for losing data for the grid, while the CFO misplaced the payroll data (as specified by the second argument in the second method call).<br \/>\n\u7531\u4e8eEnterLogData()\u7684\u7b2c\u4e00\u6b21\u8c03\u7528\u6ca1\u6709\u6307\u5b9a\u7b2c\u4e8c\u4e2a\u5b57\u7b26\u4e32\u53c2\u6570\uff0c\u56e0\u6b64\u60a8\u4f1a\u53d1\u73b0Programmer\u662f\u8d1f\u8d23\u4e22\u5931\u7f51\u683c\u6570\u636e\u7684\u4eba\uff0c\u800c CFO \u9519\u8bef\u5730\u653e\u7f6e\u4e86payroll data\uff08\u7531\u7b2c\u4e8c\u4e2a\u65b9\u6cd5\u8c03\u7528\u4e2d\u7684\u7b2c\u4e8c\u4e2a\u53c2\u6570\u6307\u5b9a\uff09\u3002<\/p>\n<p>One important thing to be aware of is that the value assigned to an optional parameter must be known at compile time and cannot be resolved at runtime (if you attempt to do so, you will receive compile-time errors!). To illustrate, assume you want to update EnterLogData() with the following extra optional parameter:<br \/>\n\u9700\u8981\u6ce8\u610f\u7684\u4e00\u4ef6\u91cd\u8981\u4e8b\u60c5\u662f\uff0c\u5206\u914d\u7ed9\u53ef\u9009\u53c2\u6570\u7684\u503c\u5fc5\u987b\u5728\u7f16\u8bd1\u65f6\u5df2\u77e5\uff0c\u5e76\u4e14\u4e0d\u80fd\u5728\u8fd0\u884c\u65f6\u89e3\u6790\uff08\u5982\u679c\u5c1d\u8bd5\u8fd9\u6837\u505a\uff0c\u5c06\u6536\u5230\u7f16\u8bd1\u65f6\u9519\u8bef\uff01\uff09\u3002\u4e3a\u4e86\u8fdb\u884c\u8bf4\u660e\uff0c\u5047\u8bbe\u60a8\u60f3\u4f7f\u7528\u4ee5\u4e0b\u989d\u5916\u7684\u53ef\u9009\u53c2\u6570\u66f4\u65b0nterLogData()\uff1a<\/p>\n<p><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n\/\/ Error! The default value for an optional arg must be known at compile time!\n\/\/ \u9519\u8bef\uff01\u5728\u7f16\u8bd1\u65f6\u5fc5\u987b\u77e5\u9053\u53ef\u9009\u53c2\u6570\u7684\u9ed8\u8ba4\u503c\n\nstatic void EnterLogData(string message, string owner = &quot;Programmer&quot;, DateTime timeStamp = DateTime.Now)\n{\n    Console.WriteLine(&quot;Error: {0}&quot;, message);\n    Console.WriteLine(&quot;Owner of Error: {0}&quot;, owner);\n    Console.WriteLine(&quot;Time of Error: {0}&quot;, timeStamp);\n}\n<\/pre>\n<\/p>\n<p>This will not compile because the value of the Now property of the DateTime class is resolved at runtime, not compile time.<br \/>\n\u8fd9\u4e0d\u4f1a\u7f16\u8bd1\uff0c\u56e0\u4e3a DateTime \u7c7b\u7684 Now \u5c5e\u6027\u7684\u503c\u662f\u5728\u8fd0\u884c\u65f6\u89e3\u6790\u7684\uff0c\u800c\u4e0d\u662f\u5728\u7f16\u8bd1\u65f6\u89e3\u6790\u7684\u3002<\/p>\n<blockquote><p>\n\u25a0Note to avoid ambiguity, optional parameters must always be placed at the end of a method signature. it is a compiler error to have optional parameters listed before nonoptional parameters.<br \/>\n\u6ce8\u610f \u4e3a\u907f\u514d\u6b67\u4e49\uff0c\u53ef\u9009\u53c2\u6570\u5fc5\u987b\u59cb\u7ec8\u653e\u5728\u65b9\u6cd5\u7b7e\u540d\u7684\u672b\u5c3e\u3002\u5728\u975e\u53ef\u9009\u53c2\u6570\u4e4b\u524d\u5217\u51fa\u53ef\u9009\u53c2\u6570\u4f1a\u51fa\u73b0\u7f16\u8bd1\u5668\u9519\u8bef\u3002\n<\/p><\/blockquote>\n<h2>Using Named Arguments (Updated 7.2)<\/h2>\n<p>\u4f7f\u7528\u547d\u540d\u53c2\u6570\uff087.2 \u66f4\u65b0\uff09<\/p>\n<p>Another language feature found in C# is support for named arguments. Named arguments allow you to invoke a method by specifying parameter values in any order you choose. Thus, rather than passing parameters solely by position (as you will do in most cases), you can choose to specify each argument by name using a colon operator. To illustrate the use of named arguments, assume you have added the following method to the Program.cs file:<br \/>\nC#\u4e2d\u7684\u53e6\u4e00\u4e2a\u8bed\u8a00\u7279\u6027\u662f\u652f\u6301\u547d\u540d\u53c2\u6570\u3002\u547d\u540d\u5b9e\u53c2\u5141\u8bb8\u60a8\u901a\u8fc7\u6309\u9009\u62e9\u7684\u4efb\u4f55\u987a\u5e8f\u6307\u5b9a\u53c2\u6570\u503c\u6765\u8c03\u7528\u65b9\u6cd5\u3002\u56e0\u6b64\uff0c\u60a8\u53ef\u4ee5\u9009\u62e9\u4f7f\u7528\u5192\u53f7\u8fd0\u7b97\u7b26\u6309\u540d\u79f0\u6307\u5b9a\u6bcf\u4e2a\u53c2\u6570\uff0c\u800c\u4e0d\u662f\u50cf\u5728\u5927\u591a\u6570\u60c5\u51b5\u4e0b\u90a3\u6837\u4ec5\u6309\u4f4d\u7f6e\u4f20\u9012\u53c2\u6570\u3002\u4e3a\u4e86\u8bf4\u660e\u547d\u540d\u53c2\u6570\u7684\u4f7f\u7528\uff0c\u5047\u8bbe\u60a8\u5df2\u5c06\u4ee5\u4e0b\u65b9\u6cd5\u6dfb\u52a0\u5230Program.cs\u6587\u4ef6\u4e2d\uff1a<\/p>\n<p><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nstatic void DisplayFancyMessage(ConsoleColor textColor,\n    ConsoleColor backgroundColor, string message)\n{\n    \/\/ Store old colors to restore after message is printed.\n    \/\/ \u5b58\u50a8\u65e7\u989c\u8272\u4ee5\u5728\u6253\u5370\u6d88\u606f\u540e\u6062\u590d\u3002\n    ConsoleColor oldTextColor = Console.ForegroundColor;\n    ConsoleColor oldbackgroundColor = Console.BackgroundColor;\n    \/\/ Set new colors and print message.\n    \/\/ \u8bbe\u7f6e\u65b0\u989c\u8272\u5e76\u6253\u5370\u6d88\u606f\u3002\n    Console.ForegroundColor = textColor;\n    Console.BackgroundColor = backgroundColor;\n    Console.WriteLine(message);\n\n    \/\/ Restore previous colors.\n    \/\/ \u6062\u590d\u4ee5\u524d\u7684\u989c\u8272\u3002\n    Console.ForegroundColor = oldTextColor;\n    Console.BackgroundColor = oldbackgroundColor;\n}\n<\/pre>\n<\/p>\n<p>Now, the way DisplayFancyMessage() was written, you would expect the caller to invoke this method by passing two ConsoleColor variables followed by a string type. However, using named arguments, the following calls are completely fine:<br \/>\n\u73b0\u5728\uff0c\u6309\u7167DisplayFancyMessage()\u7684\u7f16\u5199\u65b9\u5f0f\uff0c\u4f60\u4f1a\u671f\u671b\u8c03\u7528\u65b9\u901a\u8fc7\u4f20\u9012\u4e24\u4e2a ConsoleColor \u53d8\u91cf\u540e\u8ddf\u4e00\u4e2a\u5b57\u7b26\u4e32\u7c7b\u578b\u6765\u8c03\u7528\u6b64\u65b9\u6cd5\u3002\u4f46\u662f\uff0c\u4f7f\u7528\u547d\u540d\u53c2\u6570\uff0c\u4ee5\u4e0b\u8c03\u7528\u5b8c\u5168\u6ca1\u95ee\u9898\uff1a<\/p>\n<p><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nConsole.WriteLine(&quot;***** Fun with Methods *****&quot;);\nDisplayFancyMessage(message: &quot;Wow! Very Fancy indeed!&quot;,textColor: ConsoleColor.DarkRed,backgroundColor: ConsoleColor.White);\nDisplayFancyMessage(backgroundColor: ConsoleColor.Green,message: &quot;Testing...&quot;,textColor: ConsoleColor.DarkBlue);\nConsole.ReadLine();\n\nstatic void DisplayFancyMessage(ConsoleColor textColor,\nConsoleColor backgroundColor, string message)\n{\n    \/\/ Store old colors to restore after message is printed.\n    \/\/ \u5b58\u50a8\u65e7\u989c\u8272\u4ee5\u5728\u6253\u5370\u6d88\u606f\u540e\u6062\u590d\u3002\n    ConsoleColor oldTextColor = Console.ForegroundColor;\n    ConsoleColor oldbackgroundColor = Console.BackgroundColor;\n    \/\/ Set new colors and print message.\n    \/\/ \u8bbe\u7f6e\u65b0\u989c\u8272\u5e76\u6253\u5370\u6d88\u606f\u3002\n    Console.ForegroundColor = textColor;\n    Console.BackgroundColor = backgroundColor;\n    Console.WriteLine(message);\n\n    \/\/ Restore previous colors.\n    \/\/ \u6062\u590d\u4ee5\u524d\u7684\u989c\u8272\u3002\n    Console.ForegroundColor = oldTextColor;\n    Console.BackgroundColor = oldbackgroundColor;\n}\n<\/pre>\n<\/p>\n<p>The rules for using named arguments were updated slightly with C# 7.2. Prior to 7.2, if you begin to invoke a method using positional parameters, you must list them before any named parameters. With 7.2 and later versions of C#, named and unnamed parameters can be mingled if the parameters are in the correct position.<br \/>\n\u4f7f\u7528\u547d\u540d\u53c2\u6570\u7684\u89c4\u5219\u5728 C# 7.2 \u4e2d\u7565\u6709\u66f4\u65b0\u3002\u5728 7.2 \u4e4b\u524d\uff0c\u5982\u679c\u5f00\u59cb\u4f7f\u7528\u4f4d\u7f6e\u53c2\u6570\u8c03\u7528\u65b9\u6cd5\uff0c\u5219\u5fc5\u987b\u5728\u4efb\u4f55\u547d\u540d\u53c2\u6570\u4e4b\u524d\u5217\u51fa\u5b83\u4eec\u3002\u5bf9\u4e8e 7.2 \u53ca\u66f4\u9ad8\u7248\u672c\u7684 C#\uff0c\u5982\u679c\u53c2\u6570\u4f4d\u4e8e\u6b63\u786e\u7684\u4f4d\u7f6e\uff0c\u5219\u53ef\u4ee5\u6df7\u5408\u547d\u540d\u548c\u672a\u547d\u540d\u53c2\u6570\u3002<\/p>\n<blockquote><p>\n\u25a0Note Just because you can mix and match named arguments with positional arguments in C# 7.2 and later, it\u2019s not considered a good idea. Just because you can does not mean you should!<br \/>\n\u6ce8\u610f \u4ec5\u4ec5\u56e0\u4e3a\u60a8\u53ef\u4ee5\u5728 C# 7.2 \u53ca\u66f4\u9ad8\u7248\u672c\u4e2d\u5c06\u547d\u540d\u53c2\u6570\u4e0e\u4f4d\u7f6e\u53c2\u6570\u6df7\u5408\u548c\u5339\u914d\uff0c\u5c31\u4e0d\u662f\u4e00\u4e2a\u597d\u4e3b\u610f\u3002\u53ef\u4ee5\u8fd9\u6837\u505a\u4f46\u6ca1\u5fc5\u8981\u8fd9\u6837\u505a\u3002\n<\/p><\/blockquote>\n<p>The following code is an example:<br \/>\n\u4ee5\u4e0b\u4ee3\u7801\u662f\u4e00\u4e2a\u793a\u4f8b\uff1a<\/p>\n<p><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n\/\/ This is OK, as positional args are listed before named args.\n\/\/ \u8fd9\u662f\u6b63\u786e\u7684\uff0c\u56e0\u4e3a\u4f4d\u7f6e\u53c2\u6570\u5217\u5728\u547d\u540d\u53c2\u6570\u4e4b\u524d\u3002\nDisplayFancyMessage(ConsoleColor.Blue,message: &quot;Testing...&quot;,backgroundColor: ConsoleColor.White);\n\n\/\/ This is OK, all arguments are in the correct order\n\/\/ \u8fd9\u662f\u6b63\u786e\u7684\uff0c\u6240\u6709\u53c2\u6570\u7684\u987a\u5e8f\u90fd\u6b63\u786e\nDisplayFancyMessage(textColor: ConsoleColor.White, backgroundColor: ConsoleColor.Blue, &quot;Testing...&quot;);\n\n\/\/ This is an ERROR, as positional args are listed after named args.\n\/\/ \u8fd9\u662f\u4e00\u4e2a\u9519\u8bef\uff0c\u56e0\u4e3a\u4f4d\u7f6e\u53c2\u6570\u5217\u5728\u547d\u540d\u53c2\u6570\u4e4b\u540e\u3002\nDisplayFancyMessage(message: &quot;Testing...&quot;,backgroundColor: ConsoleColor.White,ConsoleColor.Blue);\n<\/pre>\n<\/p>\n<p>This restriction aside, you might still be wondering when you would ever want to use this language feature. After all, if you need to specify three arguments to a method, why bother flipping around their positions?<br \/>\n\u6487\u5f00\u6b64\u9650\u5236\u4e0d\u8c08\uff0c\u60a8\u53ef\u80fd\u4ecd\u7136\u60f3\u77e5\u9053\u4f55\u65f6\u8981\u4f7f\u7528\u6b64\u8bed\u8a00\u529f\u80fd\u3002\u6bd5\u7adf\uff0c\u5982\u679c\u60a8\u9700\u8981\u4e3a\u4e00\u4e2a\u65b9\u6cd5\u6307\u5b9a\u4e09\u4e2a\u53c2\u6570\uff0c\u4e3a\u4ec0\u4e48\u8981\u8d39\u5fc3\u7ffb\u8f6c\u5b83\u4eec\u7684\u4f4d\u7f6e\u5462\uff1f<\/p>\n<p>Well, as it turns out, if you have a method that defines optional arguments, this feature can be helpful.<br \/>\n\u597d\u5427\uff0c\u4e8b\u5b9e\u8bc1\u660e\uff0c\u5982\u679c\u60a8\u6709\u4e00\u4e2a\u5b9a\u4e49\u53ef\u9009\u53c2\u6570\u7684\u65b9\u6cd5\uff0c\u5219\u6b64\u529f\u80fd\u53ef\u80fd\u4f1a\u6709\u6240\u5e2e\u52a9\u3002<\/p>\n<p>Assume DisplayFancyMessage() has been rewritten to now support optional arguments, as you have assigned fitting defaults.<br \/>\n\u5047\u8bbe DisplayFancyMessage\uff08\uff09 \u5df2\u88ab\u91cd\u5199\u4e3a\u73b0\u5728\u652f\u6301\u53ef\u9009\u53c2\u6570\uff0c\u56e0\u4e3a\u60a8\u5df2\u7ecf\u5206\u914d\u4e86\u62df\u5408\u9ed8\u8ba4\u503c\u3002<\/p>\n<p><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nstatic void DisplayFancyMessage(ConsoleColor textColor = ConsoleColor.Blue,\n    ConsoleColor backgroundColor = ConsoleColor.White,\nstring message = &quot;Test Message&quot;)\n{\n    ...\n}\n<\/pre>\n<\/p>\n<p>Given that each argument has a default value, named arguments allow the caller to specify only the parameters for which they do not want to receive the defaults. Therefore, if the caller wants the value &quot;Hello!&quot; to appear in blue text surrounded by a white background, they can simply specify the following:<br \/>\n\u5047\u8bbe\u6bcf\u4e2a\u53c2\u6570\u90fd\u6709\u4e00\u4e2a\u9ed8\u8ba4\u503c\uff0c\u5219\u547d\u540d\u53c2\u6570\u5141\u8bb8\u8c03\u7528\u65b9\u4ec5\u6307\u5b9a\u4e0d\u5e0c\u671b\u63a5\u6536\u9ed8\u8ba4\u503c\u7684\u53c2\u6570\u3002\u56e0\u6b64\uff0c\u5982\u679c\u8c03\u7528\u8005\u5e0c\u671b\u503c\u201cHello\uff01\u201d\u4ee5\u767d\u8272\u80cc\u666f\u5305\u56f4\u7684\u84dd\u8272\u6587\u672c\u663e\u793a\uff0c\u4ed6\u4eec\u53ef\u4ee5\u7b80\u5355\u5730\u6307\u5b9a\u4ee5\u4e0b\u5185\u5bb9\uff1a<\/p>\n<p><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nDisplayFancyMessage(message: &quot;Hello!&quot;);\n<\/pre>\n<\/p>\n<p>Or, if the caller wants to see \u201cTest Message\u201d print out with a green background containing blue text, they can invoke the following:<br \/>\n\u6216\u8005\uff0c\u5982\u679c\u8c03\u7528\u65b9\u5e0c\u671b\u770b\u5230\u201c\u6d4b\u8bd5\u6d88\u606f\u201d\u6253\u5370\u51fa\u6765\uff0c\u80cc\u666f\u4e3a\u5305\u542b\u84dd\u8272\u6587\u672c\uff0c\u4ed6\u4eec\u53ef\u4ee5\u8c03\u7528\u4ee5\u4e0b\u5185\u5bb9\uff1a<\/p>\n<p><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nDisplayFancyMessage(backgroundColor: ConsoleColor.Green);\n<\/pre>\n<\/p>\n<p>As you can see, optional arguments and named parameters tend to work hand in hand. To wrap up your examination of building C# methods, I need to address the topic of method overloading.<br \/>\n\u6b63\u5982\u60a8\u6240\u770b\u5230\u7684\uff0c\u53ef\u9009\u53c2\u6570\u548c\u547d\u540d\u53c2\u6570\u5f80\u5f80\u662f\u534f\u540c\u5de5\u4f5c\u7684\u3002\u4e3a\u4e86\u7ed3\u675f\u60a8\u5bf9\u6784\u5efaC#\u65b9\u6cd5\u7684\u7814\u7a76\uff0c\u6211\u9700\u8981\u8ba8\u8bba\u65b9\u6cd5\u91cd\u8f7d\u7684\u4e3b\u9898\u3002<\/p>\n<h2>Understanding Method Overloading<\/h2>\n<p>\u4e86\u89e3\u65b9\u6cd5\u91cd\u8f7d<br \/>\nLike other modern object-oriented languages, C# allows a method to be overloaded. Simply put, when you define a set of identically named methods that differ by the number (or type) of parameters, the method in question is said to be overloaded.<br \/>\n\u4e0e\u5176\u4ed6\u73b0\u4ee3\u9762\u5411\u5bf9\u8c61\u8bed\u8a00\u4e00\u6837\uff0cC# \u5141\u8bb8\u91cd\u8f7d\u65b9\u6cd5\u3002\u7b80\u5355\u5730\u8bf4\uff0c\u5f53\u60a8\u5b9a\u4e49\u4e00\u7ec4\u540d\u79f0\u76f8\u540c\u7684\u65b9\u6cd5\u65f6\uff0c\u8fd9\u4e9b\u65b9\u6cd5\u56e0\u53c2\u6570\u7684\u6570\u91cf\uff08\u6216\u7c7b\u578b\uff09\u800c\u5f02\uff0c\u5219\u79f0\u4e3a\u91cd\u8f7d\u3002<\/p>\n<p>To understand why overloading is so useful, consider life as an old-school Visual Basic 6.0 (VB6) developer. Assume you are using VB6 to build a set of methods that return the sum of various incoming data types (Integers, Doubles, etc.). Given that VB6 does not support method overloading, you would be required to define a unique set of methods that essentially do the same thing (return the sum of the arguments).<br \/>\n\u8981\u7406\u89e3\u4e3a\u4ec0\u4e48\u91cd\u8f7d\u5982\u6b64\u6709\u7528\uff0c\u8bf7\u8003\u8651\u4f5c\u4e3a\u8001\u5f0f Visual Basic 6.0 \uff08VB6\uff09 \u5f00\u53d1\u4eba\u5458\u7684\u751f\u6d3b\u3002\u5047\u8bbe\u60a8\u6b63\u5728\u4f7f\u7528 VB6 \u6784\u5efa\u4e00\u7ec4\u65b9\u6cd5\uff0c\u8fd9\u4e9b\u65b9\u6cd5\u8fd4\u56de\u5404\u79cd\u4f20\u5165\u6570\u636e\u7c7b\u578b\uff08\u6574\u6570s\u3001\u53cc\u7cbe\u5ea6s \u7b49\uff09\u7684\u603b\u548c\u3002\u9274\u4e8e VB6 \u4e0d\u652f\u6301\u65b9\u6cd5\u91cd\u8f7d\uff0c\u60a8\u9700\u8981\u5b9a\u4e49\u4e00\u7ec4\u672c\u8d28\u4e0a\u6267\u884c\u76f8\u540c\u64cd\u4f5c\u7684\u552f\u4e00\u65b9\u6cd5\uff08\u8fd4\u56de\u53c2\u6570\u7684\u603b\u548c\uff09\u3002<\/p>\n<pre class=\"brush: vb; title: ; notranslate\" title=\"\">\nREM VB6 code examples.\nPublic Function AddInts(ByVal x As Integer, ByVal y As Integer) As Integer\n    AddInts = x + y\nEnd Function\nPublic Function AddDoubles(ByVal x As Double, ByVal y As Double) As Double\n    AddDoubles = x + y\nEnd Function\nPublic Function AddLongs(ByVal x As Long, ByVal y As Long) As Long\n    AddLongs = x + y\nEnd Function\n<\/pre>\n<\/p>\n<p>Not only can code such as this become tough to maintain, but the caller must now be painfully aware of the name of each method. Using overloading, you can allow the caller to call a single method named Add(). Again, the key is to ensure that each version of the method has a distinct set of arguments (methods differing only by return type are not unique enough).<br \/>\n\u50cf\u8fd9\u6837\u7684\u4ee3\u7801\u4e0d\u4ec5\u4f1a\u53d8\u5f97\u96be\u4ee5\u7ef4\u62a4\uff0c\u800c\u4e14\u8c03\u7528\u8005\u73b0\u5728\u5fc5\u987b\u75db\u82e6\u5730\u610f\u8bc6\u5230\u6bcf\u4e2a\u65b9\u6cd5\u7684\u540d\u79f0\u3002\u4f7f\u7528\u91cd\u8f7d\uff0c\u53ef\u4ee5\u5141\u8bb8\u8c03\u7528\u65b9\u8c03\u7528\u540d\u4e3aAdd()\u7684\u5355\u4e2a\u65b9\u6cd5\u3002\u540c\u6837\uff0c\u5173\u952e\u662f\u8981\u786e\u4fdd\u65b9\u6cd5\u7684\u6bcf\u4e2a\u7248\u672c\u90fd\u6709\u4e00\u7ec4\u4e0d\u540c\u7684\u53c2\u6570\uff08\u4ec5\u8fd4\u56de\u7c7b\u578b\u4e0d\u540c\u7684\u65b9\u6cd5\u4e0d\u591f\u552f\u4e00\uff09\u3002<\/p>\n<blockquote><p>\n\u25a0Note as will be explained in Chapter 10, it is possible to build generic methods that take the concept of overloading to the next level. using generics, you can define type placeholders for a method implementation that are specified at the time you invoke the member in question.<br \/>\n\u8bf7\u6ce8\u610f\uff0c\u6b63\u5982\u5c06\u5728\u7b2c 10 \u7ae0\u4e2d\u89e3\u91ca\u7684\u90a3\u6837\uff0c\u53ef\u4ee5\u6784\u5efa\u5c06\u91cd\u8f7d\u6982\u5ff5\u63d0\u5347\u5230\u4e0b\u4e00\u4e2a\u7ea7\u522b\u7684\u6cdb\u578b\u65b9\u6cd5\u3002\u4f7f\u7528\u6cdb\u578b\uff0c\u53ef\u4ee5\u4e3a\u8c03\u7528\u76f8\u5173\u6210\u5458\u65f6\u6307\u5b9a\u7684\u65b9\u6cd5\u5b9e\u73b0\u5b9a\u4e49\u7c7b\u578b\u5360\u4f4d\u7b26\u3002\n<\/p><\/blockquote>\n<p>To check this out firsthand, create a new Console Application project named FunWithMethodOverloading. Add a new class named AddOperations.cs, and update the code to the following:<br \/>\n\u82e5\u8981\u76f4\u63a5\u68c0\u67e5\u8fd9\u4e00\u70b9\uff0c\u8bf7\u521b\u5efa\u4e00\u4e2a\u540d\u4e3a FunWithMethodOverload \u7684\u65b0\u63a7\u5236\u53f0\u5e94\u7528\u7a0b\u5e8f\u9879\u76ee\u3002\u6dfb\u52a0\u4e00\u4e2a\u540d\u4e3aAddOperations.cs\u7684\u65b0\u7c7b\uff0c\u5e76\u5c06\u4ee3\u7801\u66f4\u65b0\u4e3a\u4ee5\u4e0b\u5185\u5bb9\uff1a<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nnamespace FunWithMethodOverloading;\n\/\/ C# code.\n\/\/ Overloaded Add() method.\npublic static class AddOperations\n{\n    \/\/ Overloaded Add() method.\n    \/\/ \u91cd\u8f7d\u7684Add()\u65b9\u6cd5\u3002\n    public static int Add(int x, int y)\n    {\n        return x + y;\n    }\n    public static double Add(double x, double y)\n    {\n        return x + y;\n    }\n    public static long Add(long x, long y)\n    {\n        return x + y;\n    }\n}\n<\/pre>\n<\/p>\n<p>Replace the code in the Program.cs file with the following:<br \/>\n\u5c06 Program.cs \u6587\u4ef6\u4e2d\u7684\u4ee3\u7801\u66ff\u6362\u4e3a\u4ee5\u4e0b\u5185\u5bb9\uff1a<\/p>\n<p><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nusing static FunWithMethodOverloading.AddOperations;\nConsole.WriteLine(&quot;***** Fun with Method Overloading *****\\n&quot;);\n\/\/ Calls int version of Add()\n\/\/ \u8c03\u7528int\u7248\u672c\u7684Add()\nConsole.WriteLine(Add(10, 10));\n\n\/\/ Calls long version of Add() (using the new digit separator)\n\/\/ \u8c03\u7528long\u7248\u672c\u7684Add()\uff08\u4f7f\u7528\u65b0\u7684\u6570\u5b57\u5206\u9694\u7b26\uff09\nConsole.WriteLine(Add(900_000_000_000, 900_000_000_000));\n\n\/\/ Calls double version of Add()\n\/\/ \u8c03\u7528double\u7248\u672c\u7684Add()\nConsole.WriteLine(Add(4.3, 4.4));\nConsole.ReadLine();\n<\/pre>\n<\/p>\n<blockquote><p>\n\u25a0 Note  the using static statement will be covered in Chapter 5. For now, consider it a keyboard shortcut for using methods containing a static class named AddOperations in the FunWithMethodOverloading namespace.<br \/>\n\u8bf7\u6ce8\u610f\uff0c\u4f7f\u7528 static \u8bed\u53e5\u5c06\u5728\u7b2c 5 \u7ae0\u4e2d\u4ecb\u7ecd\u3002\u73b0\u5728\uff0c\u5c06\u5176\u89c6\u4e3a\u952e\u76d8\u5feb\u6377\u952e\u3002\u65b9\u4fbf\u8c03\u7528FunWithMethodOverload\u547d\u540d\u7a7a\u95f4\u4e2d\u5305\u542b\u540d\u4e3aAddOperations \u7684\u9759\u6001\u7c7b\u7684\u65b9\u6cd5\u3002\n<\/p><\/blockquote>\n<p>The top-level statements called three different versions of the Add method, each using a different data type.<br \/>\n\u9876\u7ea7\u8bed\u53e5\u8c03\u7528 Add \u65b9\u6cd5\u7684\u4e09\u4e2a\u4e0d\u540c\u7248\u672c\uff0c\u6bcf\u4e2a\u7248\u672c\u4f7f\u7528\u4e0d\u540c\u7684\u6570\u636e\u7c7b\u578b\u3002<\/p>\n<p>Both Visual Studio and Visual Studio Code help when calling overloaded methods to boot. When you type in the name of an overloaded method (such as your good friend Console.WriteLine()), IntelliSense will list each version of the method in question. Note that you can cycle through each version of an overloaded method using the up and down arrow keys, as indicated in Figure 4-1 (Visual Studio) and Figure 4-2 (Visual Studio Code).<br \/>\nVisual Studio \u548c Visual Studio Code \u5728\u8c03\u7528\u91cd\u8f7d\u65b9\u6cd5\u8fdb\u884c\u5f15\u5bfc\u65f6\u90fd\u6709\u5e2e\u52a9\u3002\u5f53\u60a8\u952e\u5165\u91cd\u8f7d\u65b9\u6cd5\u7684\u540d\u79f0(\u4f8b\u5982 Console.WriteLine())\u65f6\uff0cIntelliSense \u5c06\u5217\u51fa\u76f8\u5173\u65b9\u6cd5\u7684\u6bcf\u4e2a\u7248\u672c\u3002\u8bf7\u6ce8\u610f\uff0c\u60a8\u53ef\u4ee5\u4f7f\u7528\u5411\u4e0a\u548c\u5411\u4e0b\u7bad\u5934\u952e\u5faa\u73af\u6d4f\u89c8\u91cd\u8f7d\u65b9\u6cd5\u7684\u6bcf\u4e2a\u7248\u672c\uff0c\u5982\u56fe 4-1 \uff08Visual Studio\uff09 \u548c\u56fe 4-2\uff08Visual Studio \u4ee3\u7801\uff09\u6240\u793a\u3002<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/miie.net\/img\/pro-csharp10\/images\/0401.jpg\" alt=\"Alt text\" \/><\/p>\n<p>Figure 4-1. Visual Studio IntelliSense for overloaded methods<br \/>\n\u56fe 4-1\u3002 Visual Studio \u91cd\u8f7d\u65b9\u6cd5\u63d0\u793a<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/miie.net\/img\/pro-csharp10\/images\/0402.jpg\" alt=\"Alt text\" \/><\/p>\n<p>Figure 4-2. Visual Studio Code IntelliSense for overloaded methods<br \/>\n\u56fe 4-2\u3002 Visual Studio Code \u91cd\u8f7d\u65b9\u6cd5\u63d0\u793a<\/p>\n<p>If your overload has optional parameters, then the compiler will pick the method that is the best match for the calling code, based on named and\/or positional arguments. Add the following method:<br \/>\n\u5982\u679c\u91cd\u8f7d\u5177\u6709\u53ef\u9009\u53c2\u6570\uff0c\u5219\u7f16\u8bd1\u5668\u5c06\u6839\u636e\u547d\u540d\u548c\uff08\u6216\uff09\u4f4d\u7f6e\u53c2\u6570\u9009\u62e9\u4e0e\u8c03\u7528\u4ee3\u7801\u6700\u5339\u914d\u7684\u65b9\u6cd5\u3002\u6dfb\u52a0\u4ee5\u4e0b\u65b9\u6cd5\uff1a<\/p>\n<p><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nstatic int Add(int x, int y, int z = 0)\n{\n    return x + (y * z);\n}\n<\/pre>\n<\/p>\n<p>If the optional argument is not passed in by the caller, the compiler will match the first signature (the one without the optional parameter). While there is a rule set for method location, it is generally not a good idea to create methods that differ only on the optional parameters.<br \/>\n\u5982\u679c\u8c03\u7528\u65b9\u672a\u4f20\u5165\u53ef\u9009\u53c2\u6570\uff0c\u5219\u7f16\u8bd1\u5668\u5c06\u5339\u914d\u7b2c\u4e00\u4e2a\u7b7e\u540d\uff08\u6ca1\u6709\u53ef\u9009\u53c2\u6570\u7684\u7b7e\u540d\uff09\u3002\u867d\u7136\u5b58\u5728\u65b9\u6cd5\u4f4d\u7f6e\u7684\u89c4\u5219\u96c6\uff0c\u4f46\u521b\u5efa\u4ec5\u5728\u53ef\u9009\u53c2\u6570\u4e0a\u4e0d\u540c\u7684\u65b9\u6cd5\u901a\u5e38\u4e0d\u662f\u4e00\u4e2a\u597d\u4e3b\u610f\u3002<\/p>\n<p>Finally, in, ref, and out are not considered as part of the signature for method overloading when more than one modifier is used. In other words, the following overloads will throw a compiler error:<br \/>\n\u6700\u540e\uff0c\u5f53\u4f7f\u7528\u591a\u4e2a\u4fee\u9970\u7b26\u65f6\uff0cin\u3001ref \u548c out \u4e0d\u88ab\u89c6\u4e3a\u65b9\u6cd5\u91cd\u8f7d\u7b7e\u540d\u7684\u4e00\u90e8\u5206\u3002\u6362\u53e5\u8bdd\u8bf4\uff0c\u4ee5\u4e0b\u91cd\u8f7d\u5c06\u5f15\u53d1\u7f16\u8bd1\u5668\u9519\u8bef\uff1a<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nstatic int Add(ref int x) { \/* *\/ }\nstatic int Add(out int x) { \/* *\/ }\n<\/pre>\n<\/p>\n<p>However, if only one method uses in, ref, or out, the compiler can distinguish between the signatures.<br \/>\n\u4f46\u662f\uff0c\u5982\u679c\u53ea\u6709\u4e00\u4e2a\u65b9\u6cd5\u4f7f\u7528 in\u3001ref \u6216 out\uff0c\u7f16\u8bd1\u5668\u53ef\u4ee5\u533a\u5206\u7b7e\u540d\u3002<\/p>\n<p>So, this is allowed:<br \/>\n\u56e0\u6b64\uff0c\u8fd9\u662f\u5141\u8bb8\u7684\uff1a<\/p>\n<p><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nstatic int Add(ref int x) { \/* *\/ }\nstatic int Add(int x) { \/* *\/ }\n<\/pre>\n<\/p>\n<p>That wraps up the initial examination of building methods using the syntax of C#. Next, let\u2019s check out how to build and manipulate enumerations and structures.<br \/>\n\u4ee5\u4e0a\u7ed3\u675f\u4e86\u4f7f\u7528 C# \u8bed\u6cd5\u5bf9\u6784\u5efa\u65b9\u6cd5\u7684\u521d\u6b65\u68c0\u67e5\u3002\u63a5\u4e0b\u6765\uff0c\u8ba9\u6211\u4eec\u770b\u770b\u5982\u4f55\u751f\u6210\u548c\u64cd\u4f5c\u679a\u4e3e\u548c\u7ed3\u6784\u3002<\/p>\n<h2>Checking Parameters for Null (Updated 10.0)<\/h2>\n<p>\u68c0\u67e5\u53c2\u6570\u662f\u5426\u4e3a null\uff0810.0 \u66f4\u65b0\uff09<\/p>\n<p>If a method parameter is nullable (e.g., a reference type\u2013like string) and required by the method body, it is considered a good programming practice to check that the parameter is not null before using it. If it is null, the method should throw an ArgumentNullException. Consider the following update to the EnterLogData() method that does just that (change is in bold):<br \/>\n\u5982\u679c\u65b9\u6cd5\u53c2\u6570\u53ef\u4ee5\u4e3anull\uff08\u4f8b\u5982\uff0c\u7c7b\u4f3c\u5f15\u7528\u7c7b\u578b\u7684\u5b57\u7b26\u4e32\uff09\u5e76\u4e14\u662f\u65b9\u6cd5\u4f53\u6240\u5fc5\u9700\u7684\uff0c\u5219\u5728\u4f7f\u7528\u8be5\u53c2\u6570\u4e4b\u524d\u68c0\u67e5\u8be5\u53c2\u6570\u662f\u5426\u4e3anull\u88ab\u8ba4\u4e3a\u662f\u4e00\u79cd\u826f\u597d\u7684\u7f16\u7a0b\u4e60\u60ef\u3002\u5982\u679c\u8be5\u53c2\u6570\u4e3anull\uff0c\u5219\u8be5\u65b9\u6cd5\u5e94\u5f15\u53d1ArgumentNullException\u3002\u8003\u8651\u4e00\u4e0bEnterLogData()\u65b9\u6cd5\u7684\u4ee5\u4e0b\u66f4\u65b0\uff0c\u8be5\u65b9\u6cd5\u5c31\u662f\u8fd9\u6837\u505a\u7684\uff08\u66f4\u6539\u4ee5\u7c97\u4f53\u663e\u793a\uff09\uff1a<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nstatic void EnterLogData(string message, string owner = &quot;Programmer&quot;)\n{\n    if (message == null)\n    {\n        throw new ArgumentNullException(message);\n    }\n    Console.WriteLine(&quot;Error: {0}&quot;, message);\n    Console.WriteLine(&quot;Owner of Error: {0}&quot;, owner);\n}\n<\/pre>\n<\/p>\n<p>Introduced in C# 10, the ArgumentNullException has an extension method to do this in one line of code:<br \/>\n\u5728 C# 10 \u4e2d\u5f15\u5165\u7684 ArgumentNullException \u6709\u4e00\u4e2a\u6269\u5c55\u65b9\u6cd5\uff0c\u53ef\u4ee5\u5728\u4e00\u884c\u4ee3\u7801\u4e2d\u6267\u884c\u6b64\u64cd\u4f5c\uff1a<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nstatic void EnterLogData(string message, string owner = &quot;Programmer&quot;)\n{\n    ArgumentNullException.ThrowIfNull(message);\n    Console.WriteLine(&quot;Error: {0}&quot;, message);\n    Console.WriteLine(&quot;Owner of Error: {0}&quot;, owner);\n}\n<\/pre>\n<\/p>\n<blockquote><p>\n\u25a0 Note  exceptions are covered in Chapter 7, and extension methods are covered in Chapter 11.<br \/>\n\u6ce8\u610f \u7a0b\u5e8f\u5f02\u5e38\u5728\u7b2c7\u7ae0\u4e2d\u4ecb\u7ecd\uff0c\u6269\u5c55\u65b9\u6cd5\u5728\u7b2c11\u7ae0\u4e2d\u4ecb\u7ecd\u3002\n<\/p><\/blockquote>\n<p>Enabling nullable reference types (covered later in this chapter) helps to ensure required reference types are not null.<br \/>\n\u542f\u7528\u53ef\u4e3a null \u7684\u5f15\u7528\u7c7b\u578b\uff08\u672c\u7ae0\u540e\u9762\u5c06\u4ecb\u7ecd\uff09\u6709\u52a9\u4e8e\u786e\u4fdd\u6240\u9700\u7684\u5f15\u7528\u7c7b\u578b\u4e0d\u4e3a null\u3002<\/p>\n<h2>Understanding the enum Type<\/h2>\n<p>\u4e86\u89e3\u679a\u4e3e\u7c7b\u578b<\/p>\n<p>Recall from Chapter 1 that the .NET Core type system is composed of classes, structures, enumerations, interfaces, and delegates. To begin exploration of these types, let\u2019s check out the role of the enumeration (or simply, enum) using a new Console Application project named FunWithEnums.<br \/>\n\u56de\u60f3\u4e00\u4e0b\u7b2c 1 \u7ae0\uff0c.NET Core \u7c7b\u578b\u7cfb\u7edf\u7531\u7c7b\u3001\u7ed3\u6784\u3001\u679a\u4e3e\u3001\u63a5\u53e3\u548c\u59d4\u6258\u7ec4\u6210\u3002\u82e5\u8981\u5f00\u59cb\u63a2\u7d22\u8fd9\u4e9b\u7c7b\u578b\uff0c\u8ba9\u6211\u4eec\u4f7f\u7528\u540d\u4e3a FunWithEnums \u7684\u65b0\u63a7\u5236\u53f0\u5e94\u7528\u7a0b\u5e8f\u9879\u76ee\u68c0\u67e5\u679a\u4e3e\uff08\u6216\u7b80\u5355\u5730\u79f0\u4e3a\u679a\u4e3e\uff09\u7684\u4f5c\u7528\u3002<\/p>\n<p>\u25a0   Note  Do not confuse the term enum with enumerator; they are completely different concepts. an enum is a custom data type of name-value pairs. an enumerator is a class or structure that implements a .net Core interface named IEnumerable. typically, this interface is implemented on collection classes, as well as the System.Array class. as you will see in Chapter 8, objects that support IEnumerable can work within the foreach loop.<br \/>\n\u6ce8\u610f \u4e0d\u8981\u5c06\u672f\u8bed\u679a\u4e3e\u4e0e\u679a\u4e3e\u5668\u6df7\u6dc6;\u5b83\u4eec\u662f\u5b8c\u5168\u4e0d\u540c\u7684\u6982\u5ff5\u3002\u679a\u4e3e\u662f\u540d\u79f0-\u503c\u5bf9\u7684\u81ea\u5b9a\u4e49\u6570\u636e\u7c7b\u578b\u3002\u679a\u4e3e\u5668\u662f\u5b9e\u73b0\u540d\u4e3a IEnumerable \u7684 .NET Core \u63a5\u53e3\u7684\u7c7b\u6216\u7ed3\u6784\u3002\u901a\u5e38\uff0c\u6b64\u63a5\u53e3\u5728\u96c6\u5408\u7c7b\u4ee5\u53ca System.Array \u7c7b\u4e0a\u5b9e\u73b0\u3002 \u6b63\u5982\u60a8\u5c06\u5728\u7b2c 8 \u7ae0\u4e2d\u770b\u5230\u7684\uff0c\u652f\u6301 IEnumerable \u7684\u5bf9\u8c61\u53ef\u4ee5\u5728 foreach \u5faa\u73af\u4e2d\u5de5\u4f5c\u3002<\/p>\n<p>When building a system, it is often convenient to create a set of symbolic names that map to known numerical values. For example, if you are creating a payroll system, you might want to refer to the type of employees using constants such as vice president, manager, contractor, and grunt. C# supports the notion of custom enumerations for this very reason. For example, here is an enumeration named EmpTypeEnum (you can define this in the same file as your top-level statements, if it is placed at the end of the file):<br \/>\n\u6784\u5efa\u7cfb\u7edf\u65f6\uff0c\u521b\u5efa\u4e00\u7ec4\u6620\u5c04\u5230\u5df2\u77e5\u6570\u503c\u7684\u7b26\u53f7\u540d\u79f0\u901a\u5e38\u5f88\u65b9\u4fbf\u3002\u4f8b\u5982\uff0c\u5982\u679c\u8981\u521b\u5efa\u5de5\u8d44\u5355\u7cfb\u7edf\uff0c\u5219\u53ef\u80fd\u9700\u8981\u4f7f\u7528\u5e38\u91cf\uff08\u5982\u526f\u603b\u88c1\u3001\u7ecf\u7406\u3001\u627f\u5305\u5546\u548c\u5495\u565c\u58f0\uff09\u6765\u5f15\u7528\u5458\u5de5\u7c7b\u578b\u3002\u6b63\u662f\u51fa\u4e8e\u8fd9\u4e2a\u539f\u56e0\uff0cC# \u652f\u6301\u81ea\u5b9a\u4e49\u679a\u4e3e\u7684\u6982\u5ff5\u3002\u4f8b\u5982\uff0c\u4e0b\u9762\u662f\u4e00\u4e2a\u540d\u4e3a EmpTypeEnum \u7684\u679a\u4e3e\uff08\u5982\u679c\u5b83\u653e\u5728\u6587\u4ef6\u672b\u5c3e\uff0c\u5219\u53ef\u4ee5\u5728\u4e0e\u9876\u7ea7\u8bed\u53e5\u76f8\u540c\u7684\u6587\u4ef6\u4e2d\u5b9a\u4e49\u5b83\uff09\uff1a<\/p>\n<p>Console.WriteLine(&quot;<em><strong><\/em> Fun with Enums ***<\/strong>\\n&quot;); Console.ReadLine();<\/p>\n<p>\/\/local functions go here:<\/p>\n<p>\/\/ A custom enumeration. enum EmpTypeEnum<br \/>\n{<br \/>\nManager,   \/\/ = 0<br \/>\nGrunt, \/\/ = 1 Contractor, \/\/ = 2 VicePresident \/\/ = 3<br \/>\n}<\/p>\n<p>\u25a0   Note  By convention, enum types are usually suffixed with Enum. this is not necessary but makes for more readable code.<br \/>\n\u6ce8\u610f \u6309\u7167\u60ef\u4f8b\uff0c\u679a\u4e3e\u7c7b\u578b\u901a\u5e38\u4ee5 Enum \u4e3a\u540e\u7f00\u3002\u8fd9\u4e0d\u662f\u5fc5\u9700\u7684\uff0c\u4f46\u4f1a\u4f7f\u4ee3\u7801\u66f4\u5177\u53ef\u8bfb\u6027\u3002<\/p>\n<p>The EmpTypeEnum enumeration defines four named constants, corresponding to discrete numerical values. By default, the first element is set to the value zero (0), followed by an n+1 progression. You are free to change the initial value as you see fit. For example, if it made sense to number the members of EmpTypeEnum as 102 through 105, you could do so as follows:<br \/>\n\u679a\u4e3e\u5b9a\u4e49\u4e86\u56db\u4e2a\u547d\u540d\u5e38\u91cf\uff0c\u5bf9\u5e94\u4e8e\u79bb\u6563\u6570\u503c\u3002\u9ed8\u8ba4\u60c5\u51b5\u4e0b\uff0c\u7b2c\u4e00\u4e2a\u5143\u7d20\u8bbe\u7f6e\u4e3a\u503c\u96f6 \uff080\uff09\uff0c\u540e\u8ddf n+1 \u7ea7\u6570\u3002\u60a8\u53ef\u4ee5\u6839\u636e\u9700\u8981\u81ea\u7531\u66f4\u6539\u521d\u59cb\u503c\u3002\u4f8b\u5982\uff0c\u5982\u679c\u5c06 EmpTypeEnum \u7684\u6210\u5458\u7f16\u53f7\u4e3a 102 \u5230 105 \u662f\u6709\u610f\u4e49\u7684\uff0c\u5219\u53ef\u4ee5\u6309\u5982\u4e0b\u65b9\u5f0f\u6267\u884c\u6b64\u64cd\u4f5c\uff1a<\/p>\n<p>\/\/ Begin with 102. enum EmpTypeEnum<br \/>\n{<br \/>\nManager = 102,<br \/>\nGrunt, \/\/ = 103 Contractor, \/\/ = 104 VicePresident \/\/ = 105<br \/>\n}<\/p>\n<p>Enumerations do not necessarily need to follow a sequential ordering and do not need to have unique values. If (for some reason or another) it makes sense to establish your EmpTypeEnum as shown here, the compiler continues to be happy:<br \/>\n\u679a\u4e3e\u4e0d\u4e00\u5b9a\u9700\u8981\u9075\u5faa\u987a\u5e8f\u6392\u5e8f\uff0c\u4e5f\u4e0d\u9700\u8981\u5177\u6709\u552f\u4e00\u503c\u3002\u5982\u679c\uff08\u51fa\u4e8e\u67d0\u79cd\u539f\u56e0\uff09\u5efa\u7acb EmpTypeEnum \u662f\u6709\u610f\u4e49\u7684\uff0c\u5982\u4e0b\u6240\u793a\uff0c\u7f16\u8bd1\u5668\u4f1a\u7ee7\u7eed\u5f88\u9ad8\u5174\uff1a<\/p>\n<p>\/\/ Elements of an enumeration need not be sequential!<br \/>\n\/\/ \u679a\u4e3e\u7684\u5143\u7d20\u4e0d\u5fc5\u662f\u8fde\u7eed\u7684\uff01<br \/>\nenum EmpTypeEnum<br \/>\n{<br \/>\nManager = 10,<br \/>\nGrunt = 1,<br \/>\nContractor = 100,<br \/>\nVicePresident = 9<br \/>\n}<\/p>\n<h2>Controlling the Underlying Storage for an enum<\/h2>\n<p>\u63a7\u5236\u679a\u4e3e\u7684\u57fa\u7840\u5b58\u50a8<\/p>\n<p>By default, the storage type used to hold the values of an enumeration is a System.Int32 (the C# int); however, you are free to change this to your liking. C# enumerations can be defined in a similar manner for any of the core system types (byte, short, int, or long). For example, if you want to set the underlying storage value of EmpTypeEnum to be a byte rather than an int, you can write the following:<br \/>\n\u9ed8\u8ba4\u60c5\u51b5\u4e0b\uff0c\u7528\u4e8e\u4fdd\u5b58\u679a\u4e3e\u503c\u7684\u5b58\u50a8\u7c7b\u578b\u662f System.Int32\uff08C# int\uff09;\u4f46\u662f\uff0c\u60a8\u53ef\u4ee5\u6839\u636e\u81ea\u5df1\u7684\u559c\u597d\u81ea\u7531\u66f4\u6539\u6b64\u8bbe\u7f6e\u3002C# \u679a\u4e3e\u53ef\u4ee5\u4ee5\u7c7b\u4f3c\u7684\u65b9\u5f0f\u4e3a\u4efb\u4f55\u6838\u5fc3\u7cfb\u7edf\u7c7b\u578b\uff08\u5b57\u8282\u3001\u77ed\u6574\u578b\u3001\u6574\u578b\u6216\u6574\u578b\uff09\u5b9a\u4e49\u3002\u4f8b\u5982\uff0c\u5982\u679c\u8981\u5c06 EmpTypeEnum \u7684\u57fa\u7840\u5b58\u50a8\u503c\u8bbe\u7f6e\u4e3a\u5b57\u8282\u800c\u4e0d\u662f\u6574\u6570\uff0c\u5219\u53ef\u4ee5\u7f16\u5199\u4ee5\u4e0b\u5185\u5bb9\uff1a<\/p>\n<p>\/\/ This time, EmpTypeEnum maps to an underlying byte.<br \/>\n\u8fd9\u4e00\u6b21\uff0cEmpTypeEnum \u6620\u5c04\u5230\u5e95\u5c42\u5b57\u8282\u3002<\/p>\n<p>enum EmpTypeEnum : byte<br \/>\n{<br \/>\nManager = 10,<br \/>\nGrunt = 1,<br \/>\nContractor = 100,<br \/>\nVicePresident = 9<br \/>\n}<\/p>\n<p>Changing the underlying type of an enumeration can be helpful if you are building a .NET Core application that will be deployed to a low-memory device and need to conserve memory wherever possible. Of course, if you do establish your enumeration to use a byte as storage, each value must be within its range! For example, the following version of EmpTypeEnum will result in a compiler error, as the value 999 cannot fit within the range of a byte:<br \/>\n\u5982\u679c\u8981\u751f\u6210\u5c06\u90e8\u7f72\u5230\u4f4e\u5185\u5b58\u8bbe\u5907\u7684 .NET Core \u5e94\u7528\u7a0b\u5e8f\uff0c\u5e76\u4e14\u9700\u8981\u5c3d\u53ef\u80fd\u8282\u7701\u5185\u5b58\uff0c\u5219\u66f4\u6539\u679a\u4e3e\u7684\u57fa\u7840\u7c7b\u578b\u4f1a\u5f88\u6709\u5e2e\u52a9\u3002\u5f53\u7136\uff0c\u5982\u679c\u60a8\u786e\u5b9e\u5efa\u7acb\u4e86\u4f7f\u7528\u5b57\u8282\u4f5c\u4e3a\u5b58\u50a8\u7684\u679a\u4e3e\uff0c\u5219\u6bcf\u4e2a\u503c\u90fd\u5fc5\u987b\u5728\u5176\u8303\u56f4\u5185\uff01\u4f8b\u5982\uff0c\u4ee5\u4e0b\u7248\u672c\u7684 EmpTypeEnum \u5c06\u5bfc\u81f4\u7f16\u8bd1\u5668\u9519\u8bef\uff0c\u56e0\u4e3a\u503c 999 \u65e0\u6cd5\u5bb9\u7eb3\u5728\u5b57\u8282\u8303\u56f4\u5185\uff1a<\/p>\n<p>\/\/ Compile-time error! 999 is too big for a byte!<br \/>\n\/\/ \u7f16\u8bd1\u65f6\u9519\u8bef\uff01999\u5bf9\u4e8e\u4e00\u4e2a\u5b57\u8282\u6765\u8bf4\u592a\u5927\u4e86\uff01<br \/>\nenum EmpTypeEnum : byte<br \/>\n{<br \/>\nManager = 10,<br \/>\nGrunt = 1,<br \/>\nContractor = 100,<br \/>\nVicePresident = 999<br \/>\n}<\/p>\n<h2>Declaring enum Variables<\/h2>\n<p>\u58f0\u660e\u679a\u4e3e\u53d8\u91cf<\/p>\n<p>Once you have established the range and storage type of your enumeration, you can use it in place of so- called magic numbers. Because enumerations are nothing more than a user-defined data type, you can use them as function return values, method parameters, local variables, and so forth. Assume you have a<br \/>\nmethod named AskForBonus(), taking an EmpTypeEnum variable as the sole parameter. Based on the value of the incoming parameter, you will print out a fitting response to the pay bonus request.<br \/>\n\u786e\u5b9a\u679a\u4e3e\u7684\u8303\u56f4\u548c\u5b58\u50a8\u7c7b\u578b\u540e\uff0c\u53ef\u4ee5\u4f7f\u7528\u5b83\u4ee3\u66ff\u6240\u8c13\u7684\u5e7b\u6570\u3002\u7531\u4e8e\u679a\u4e3e\u53ea\u4e0d\u8fc7\u662f\u7528\u6237\u5b9a\u4e49\u7684\u6570\u636e\u7c7b\u578b\uff0c\u56e0\u6b64\u53ef\u4ee5\u5c06\u5b83\u4eec\u7528\u4f5c\u51fd\u6570\u8fd4\u56de\u503c\u3001\u65b9\u6cd5\u53c2\u6570\u3001\u5c40\u90e8\u53d8\u91cf\u7b49\u3002\u5047\u8bbe\u60a8\u6709\u4e00\u4e2a\u540d\u4e3a AskForBonus\uff08\uff09 \u7684\u65b9\u6cd5\uff0c\u5c06 EmpTypeEnum \u53d8\u91cf\u4f5c\u4e3a\u552f\u4e00\u53c2\u6570\u3002\u6839\u636e\u4f20\u5165\u53c2\u6570\u7684\u503c\uff0c\u60a8\u5c06\u6253\u5370\u51fa\u5bf9\u652f\u4ed8\u5956\u91d1\u8bf7\u6c42\u7684\u5408\u9002\u54cd\u5e94\u3002<\/p>\n<p>Console.WriteLine(&quot;<em><strong><\/em> Fun with Enums ***<\/strong>&quot;);<br \/>\n\/\/ Make an EmpTypeEnum variable. EmpTypeEnum emp = EmpTypeEnum.Contractor; AskForBonus(emp);<br \/>\nConsole.ReadLine();<\/p>\n<p>\/\/ Enums as parameters.<br \/>\nstatic void AskForBonus(EmpTypeEnum e)<br \/>\n{<br \/>\nswitch (e)<br \/>\n{<br \/>\ncase EmpTypeEnum.Manager:<\/p>\n<p>Console.WriteLine(&quot;How about stock options instead?&quot;); break;<br \/>\ncase EmpTypeEnum.Grunt:<br \/>\nConsole.WriteLine(&quot;You have got to be kidding...&quot;); break;<br \/>\ncase EmpTypeEnum.Contractor:<br \/>\nConsole.WriteLine(&quot;You already get enough cash...&quot;); break;<br \/>\ncase EmpTypeEnum.VicePresident: Console.WriteLine(&quot;VERY GOOD, Sir!&quot;); break;<br \/>\n}<br \/>\n}<\/p>\n<p>Notice that when you are assigning a value to an enum variable, you must scope the enum name (EmpTypeEnum) to the value (Grunt). Because enumerations are a fixed set of name-value pairs, it is illegal to set an enum variable to a value that is not defined directly by the enumerated type.<br \/>\n\u8bf7\u6ce8\u610f\uff0c\u4e3a\u679a\u4e3e\u53d8\u91cf\u8d4b\u503c\u65f6\uff0c\u5fc5\u987b\u5c06\u679a\u4e3e\u540d\u79f0 \uff08EmpTypeEnum\uff09 \u7684\u4f5c\u7528\u57df\u9650\u5b9a\u4e3a\u503c \uff08Grunt\uff09\u3002\u7531\u4e8e\u679a\u4e3e\u662f\u4e00\u7ec4\u56fa\u5b9a\u7684\u540d\u79f0-\u503c\u5bf9\uff0c\u56e0\u6b64\u5c06\u679a\u4e3e\u53d8\u91cf\u8bbe\u7f6e\u4e3a\u4e0d\u662f\u7531\u679a\u4e3e\u7c7b\u578b\u76f4\u63a5\u5b9a\u4e49\u7684\u503c\u662f\u975e\u6cd5\u7684\u3002<\/p>\n<p>static void ThisMethodWillNotCompile()<br \/>\n{<br \/>\n\/\/ Error! SalesManager is not in the EmpTypeEnum enum! EmpTypeEnum emp = EmpTypeEnum.SalesManager;<\/p>\n<p>\/\/ Error! Forgot to scope Grunt value to EmpTypeEnum enum! emp = Grunt;<br \/>\n}<\/p>\n<h2>Using the System.Enum Type<\/h2>\n<p>\u4f7f\u7528 System.Enum \u7c7b\u578b<\/p>\n<p>The interesting thing about .NET Core enumerations is that they gain functionality from the System. Enum class type. This class defines several methods that allow you to interrogate and transform a given enumeration. One helpful method is the static Enum.GetUnderlyingType(), which, as the name implies,<br \/>\nreturns the data type used to store the values of the enumerated type (System.Byte in the case of the current<br \/>\nEmpTypeEnum declaration).<br \/>\n\u5173\u4e8e .NET Core \u679a\u4e3e\u7684\u6709\u8da3\u4e4b\u5904\u5728\u4e8e\u5b83\u4eec\u4ece\u7cfb\u7edf\u83b7\u5f97\u529f\u80fd\u3002\u679a\u4e3e\u7c7b\u7c7b\u578b\u3002\u6b64\u7c7b\u5b9a\u4e49\u4e86\u51e0\u4e2a\u65b9\u6cd5\uff0c\u5141\u8bb8\u60a8\u67e5\u8be2\u548c\u8f6c\u6362\u7ed9\u5b9a\u7684\u679a\u4e3e\u3002\u4e00\u4e2a\u6709\u7528\u7684\u65b9\u6cd5\u662f\u9759\u6001 Enum.GetUnderlyingType\uff08\uff09\uff0c\u987e\u540d\u601d\u4e49\uff0c\u8fd4\u56de\u7528\u4e8e\u5b58\u50a8\u679a\u4e3e\u7c7b\u578b\u503c\u7684\u6570\u636e\u7c7b\u578b\uff08\u5728\u5f53\u524d\u7684\u60c5\u51b5\u4e0b\u4e3a System.Byte EmpTypeEnum \u58f0\u660e\uff09\u3002<\/p>\n<p>Console.WriteLine(&quot;<em><strong><\/em> Fun with Enums ***<\/strong>&quot;);<br \/>\n...<\/p>\n<p>\/\/ Print storage for the enum. Console.WriteLine(&quot;EmpTypeEnum uses a {0} for storage&quot;,<br \/>\nEnum.GetUnderlyingType(emp.GetType())); Console.ReadLine();<\/p>\n<p>The Enum.GetUnderlyingType() method requires you to pass in a System.Type as the first parameter.<\/p>\n<p>As fully examined in Chapter 17, Type represents the metadata description of a given .NET Core entity.<br \/>\n\u5982\u7b2c 17 \u7ae0\u6240\u8ff0\uff0cType \u8868\u793a\u7ed9\u5b9a .NET Core \u5b9e\u4f53\u7684\u5143\u6570\u636e\u63cf\u8ff0\u3002<\/p>\n<p>One possible way to obtain metadata (as shown previously) is to use the GetType() method, which is common to all types in the .NET Core base class libraries. Another approach is to use the C# typeof operator. One benefit of doing so is that you do not need to have a variable of the entity you want to obtain a metadata description of.<br \/>\n\u83b7\u53d6\u5143\u6570\u636e\u7684\u4e00\u79cd\u53ef\u80fd\u65b9\u6cd5\u662f\u4f7f\u7528 GetType\uff08\uff09 \u65b9\u6cd5\uff0c\u8be5\u65b9\u6cd5\u5bf9\u4e8e .NET Core \u57fa\u7c7b\u5e93\u4e2d\u7684\u6240\u6709\u7c7b\u578b\u90fd\u662f\u901a\u7528\u7684\u3002\u53e6\u4e00\u79cd\u65b9\u6cd5\u662f\u4f7f\u7528 C# \u7c7b\u578b\u7b97\u5b50\u3002\u8fd9\u6837\u505a\u7684\u4e00\u4e2a\u597d\u5904\u662f\uff0c\u60a8\u4e0d\u9700\u8981\u5177\u6709\u8981\u83b7\u53d6\u5176\u5143\u6570\u636e\u63cf\u8ff0\u7684\u5b9e\u4f53\u7684\u53d8\u91cf\u3002<\/p>\n<p>\/\/ This time use typeof to extract a Type. Console.WriteLine(&quot;EmpTypeEnum uses a {0} for storage&quot;,<br \/>\nEnum.GetUnderlyingType(typeof(EmpTypeEnum)));<\/p>\n<h2>Dynamically Discovering an enum\u2019s Name-Value Pairs<\/h2>\n<p>\u52a8\u6001\u53d1\u73b0\u679a\u4e3e\u7684\u540d\u79f0-\u503c\u5bf9<\/p>\n<p>Beyond the Enum.GetUnderlyingType() method, all C# enumerations support a method named ToString(), which returns the string name of the current enumeration\u2019s value. The following code is an example:<br \/>\n\u9664\u4e86 Enum.GetUnderlyingType\uff08\uff09 \u65b9\u6cd5\u4e4b\u5916\uff0c\u6240\u6709 C# \u679a\u4e3e\u90fd\u652f\u6301\u540d\u4e3a ToString\uff08\uff09 \u7684\u65b9\u6cd5\uff0c\u8be5\u65b9\u6cd5\u8fd4\u56de\u5f53\u524d\u679a\u4e3e\u503c\u7684\u5b57\u7b26\u4e32\u540d\u79f0\u3002\u4ee5\u4e0b\u4ee3\u7801\u662f\u4e00\u4e2a\u793a\u4f8b\uff1a<\/p>\n<p>EmpTypeEnum emp = EmpTypeEnum.Contractor;<br \/>\n...<br \/>\n\/\/ Prints out &quot;emp is a Contractor&quot;.<br \/>\n\/\/ \u6253\u5370\u51fa\u201cemp \u662f\u627f\u5305\u5546\u201d\u3002<br \/>\nConsole.WriteLine(&quot;emp is a {0}.&quot;, emp.ToString()); Console.ReadLine();<\/p>\n<p>If you are interested in discovering the value of a given enumeration variable, rather than its name, you can simply cast the enum variable against the underlying storage type. The following is an example:<br \/>\n\u5982\u679c\u6709\u5174\u8da3\u53d1\u73b0\u7ed9\u5b9a\u679a\u4e3e\u53d8\u91cf\u7684\u503c\uff0c\u800c\u4e0d\u662f\u5176\u540d\u79f0\uff0c\u53ea\u9700\u5c06\u679a\u4e3e\u53d8\u91cf\u5f3a\u5236\u8f6c\u6362\u4e3a\u57fa\u7840\u5b58\u50a8\u7c7b\u578b\u5373\u53ef\u3002\u4e0b\u9762\u662f\u4e00\u4e2a\u793a\u4f8b\uff1a<\/p>\n<p>Console.WriteLine(&quot;<em><strong><\/em> Fun with Enums ***<\/strong>&quot;); EmpTypeEnum emp = EmpTypeEnum.Contractor;<br \/>\n...<br \/>\n\/\/ Prints out &quot;Contractor = 100&quot;.<br \/>\nConsole.WriteLine(&quot;{0} = {1}&quot;, emp.ToString(), (byte)emp); Console.ReadLine();<\/p>\n<p>\u25a0   Note  the static Enum.Format() method provides a finer level of formatting options by specifying a desired format flag. Consult the documentation for a full list of formatting flags.<br \/>\n\u8bf7\u6ce8\u610f\uff0c\u9759\u6001 Enum.Format\uff08\uff09 \u65b9\u6cd5\u901a\u8fc7\u6307\u5b9a\u6240\u9700\u7684\u683c\u5f0f\u6807\u5fd7\u6765\u63d0\u4f9b\u66f4\u7cbe\u7ec6\u7684\u683c\u5f0f\u9009\u9879\u3002\u6709\u5173\u683c\u5f0f\u6807\u5fd7\u7684\u5b8c\u6574\u5217\u8868\uff0c\u8bf7\u53c2\u9605\u6587\u6863\u3002<\/p>\n<p>System.Enum also defines another static method named GetValues(). This method returns an instance of System.Array. Each item in the array corresponds to a member of the specified enumeration. Consider the following method, which will print out each name-value pair within any enumeration you pass in as a parameter:<br \/>\nSystem.Enum \u8fd8\u5b9a\u4e49\u4e86\u53e6\u4e00\u4e2a\u540d\u4e3a GetValues\uff08\uff09 \u7684\u9759\u6001\u65b9\u6cd5\u3002\u6b64\u65b9\u6cd5\u8fd4\u56de System.Array \u7684\u5b9e\u4f8b\u3002\u6570\u7ec4\u4e2d\u7684\u6bcf\u4e2a\u9879\u5bf9\u5e94\u4e8e\u6307\u5b9a\u679a\u4e3e\u7684\u6210\u5458\u3002\u8bf7\u8003\u8651\u4ee5\u4e0b\u65b9\u6cd5\uff0c\u8be5\u65b9\u6cd5\u5c06\u6253\u5370\u51fa\u4f5c\u4e3a\u53c2\u6570\u4f20\u5165\u7684\u4efb\u4f55\u679a\u4e3e\u4e2d\u7684\u6bcf\u4e2a\u540d\u79f0-\u503c\u5bf9\uff1a<\/p>\n<p>\/\/ This method will print out the details of any enum.<br \/>\n\/\/ \u6b64\u65b9\u6cd5\u5c06\u6253\u5370\u51fa\u4efb\u4f55\u679a\u4e3e\u7684\u8be6\u7ec6\u4fe1\u606f\u3002<br \/>\nstatic void EvaluateEnum(System.Enum e)<br \/>\n{<br \/>\nConsole.WriteLine(&quot;=&gt; Information about {0}&quot;, e.GetType().Name);<\/p>\n<p>Console.WriteLine(&quot;Underlying storage type: {0}&quot;, Enum.GetUnderlyingType(e.GetType()));<\/p>\n<p>\/\/ Get all name-value pairs for incoming parameter. Array enumData = Enum.GetValues(e.GetType());<br \/>\nConsole.WriteLine(&quot;This enum has {0} members.&quot;, enumData.Length);<\/p>\n<p>\/\/ Now show the string name and associated value, using the D format<br \/>\n\/\/ flag (see Chapter 3).<br \/>\nfor(int i = 0; i &lt; enumData.Length; i++)<\/p>\n<p>{<br \/>\nConsole.WriteLine(&quot;Name: {0}, Value: {0:D}&quot;, enumData.GetValue(i));<br \/>\n}<br \/>\n}<\/p>\n<p>To test this new method, update your code to create variables of several enumeration types declared in the System namespace (as well as an EmpTypeEnum enumeration for good measure). The following code is an example:<br \/>\n\u82e5\u8981\u6d4b\u8bd5\u6b64\u65b0\u65b9\u6cd5\uff0c\u8bf7\u66f4\u65b0\u4ee3\u7801\u4ee5\u521b\u5efa\u5728 System \u547d\u540d\u7a7a\u95f4\u4e2d\u58f0\u660e\u7684\u591a\u4e2a\u679a\u4e3e\u7c7b\u578b\u7684\u53d8\u91cf\uff08\u4ee5\u53ca\u7528\u4e8e\u826f\u597d\u5ea6\u91cf\u7684 EmpTypeEnum \u679a\u4e3e\uff09\u3002\u4ee5\u4e0b\u4ee3\u7801\u662f\u4e00\u4e2a\u793a\u4f8b\uff1a<\/p>\n<p>Console.WriteLine(&quot;<em><strong><\/em> Fun with Enums ***<\/strong>&quot;);<br \/>\n...<br \/>\nEmpTypeEnum e2 = EmpTypeEnum.Contractor;<\/p>\n<p>\/\/ These types are enums in the System namespace. DayOfWeek day = DayOfWeek.Monday;<br \/>\nConsoleColor cc = ConsoleColor.Gray;<\/p>\n<p>EvaluateEnum(e2);<br \/>\nEvaluateEnum(day);<br \/>\nEvaluateEnum(cc);<br \/>\nConsole.ReadLine();<\/p>\n<p>Some partial output is shown here:<br \/>\n\u6b64\u5904\u663e\u793a\u4e86\u4e00\u4e9b\u90e8\u5206\u8f93\u51fa\uff1a<br \/>\n=&gt; Information about DayOfWeek<\/p>\n<p>Underlying storage type: System.Int32 This enum has 7 members.<\/p>\n<p>Name: Sunday, Value: 0 Name: Monday, Value: 1 Name: Tuesday, Value: 2 Name: Wednesday, Value: 3 Name: Thursday, Value: 4 Name: Friday, Value: 5 Name: Saturday, Value: 6<\/p>\n<p>As you will see over the course of this text, enumerations are used extensively throughout the .NET Core base class libraries. When you make use of any enumeration, always remember that you can interact with the name-value pairs using the members of System.Enum.<br \/>\n\u6b63\u5982\u60a8\u5c06\u5728\u672c\u6587\u4e2d\u770b\u5230\u7684\u90a3\u6837\uff0c\u679a\u4e3e\u5728\u6574\u4e2a .NET Core \u57fa\u7c7b\u5e93\u4e2d\u88ab\u5e7f\u6cdb\u4f7f\u7528\u3002\u4f7f\u7528\u4efb\u4f55\u679a\u4e3e\u65f6\uff0c\u8bf7\u59cb\u7ec8\u8bb0\u4f4f\uff0c\u53ef\u4ee5\u4f7f\u7528 System.Ene \u7684\u6210\u5458\u4e0e\u540d\u79f0-\u503c\u5bf9\u8fdb\u884c\u4ea4\u4e92\u3002<\/p>\n<h2>Using Enums, Flags, and Bitwise Operations<\/h2>\n<p>\u4f7f\u7528\u679a\u4e3e\u3001\u6807\u5fd7\u548c\u6309\u4f4d\u8fd0\u7b97<\/p>\n<p>Bitwise operations provide a fast mechanism for operating on binary numbers at the bit level. Table 4-3 contains the C# bitwise operators, what they do, and an example of each.<br \/>\n\u6309\u4f4d\u8fd0\u7b97\u63d0\u4f9b\u4e86\u4e00\u79cd\u5728\u4f4d\u7ea7\u522b\u5bf9\u4e8c\u8fdb\u5236\u6570\u8fdb\u884c\u64cd\u4f5c\u7684\u5feb\u901f\u673a\u5236\u3002\u8868 4-3 \u5305\u542b C# \u6309\u4f4d\u8fd0\u7b97\u7b26\u3001\u5b83\u4eec\u7684\u4f5c\u7528\u4ee5\u53ca\u6bcf\u4e2a\u8fd0\u7b97\u7b26\u7684\u793a\u4f8b\u3002<\/p>\n<p>Table 4-3. Bitwise Operations<br \/>\n\u8868 4-3. \u6309\u4f4d\u8fd0\u7b97<\/p>\n<table>\n<tr>\n<td>Operator<\/td>\n<td>Operation<\/td>\n<td>Example<\/td>\n<\/tr>\n<tr>\n<td>&amp; (AND)<\/td>\n<td>Copies a bit if it exists in both operands<br \/>\n        \u590d\u5236\u4f4d\uff08\u5982\u679c\u4e24\u4e2a\u64cd\u4f5c\u6570\u4e2d\u90fd\u5b58\u5728\uff09<\/td>\n<td>0110 &amp; 0100 = 0100 (4)<\/td>\n<\/tr>\n<tr>\n<td>| (OR)<\/td>\n<td>Copies a bit if it exists in both operands<br \/>\n        \u590d\u5236\u4f4d\uff08\u5982\u679c\u4e24\u4e2a\u64cd\u4f5c\u6570\u4e2d\u90fd\u5b58\u5728\uff09<\/td>\n<td>0110 | 0100 = 0110 (6)<\/td>\n<\/tr>\n<tr>\n<td>^ (XOR)<\/td>\n<td>Copies a bit if it exists in one but not both operands<br \/>\n            \u5982\u679c\u4f4d\u5b58\u5728\u4e8e\u4e00\u4e2a\u64cd\u4f5c\u6570\u4e2d\uff0c\u5219\u590d\u5236\u4f4d\uff0c\u4f46\u4e0d\u662f\u4e24\u4e2a\u64cd\u4f5c\u6570<\/td>\n<td>0110 ^ 0100 = 0010 (2)<\/td>\n<\/tr>\n<tr>\n<td>~ (ones\u2019 compliment)<\/td>\n<td>Flips the bits<br \/>\n        \u7ffb\u8f6c\u4f4d<\/td>\n<td>~0110 = -7 (due to overflow)<\/td>\n<\/tr>\n<tr>\n<td>&lt;&lt; (left shift)<\/td>\n<td>Shifts the bits left<br \/>\n        \u5411\u5de6\u79fb\u52a8\u4f4d<\/td>\n<td>0110 &lt;&lt; 1 = 1100 (12)<\/td>\n<\/tr>\n<tr>\n<td>&gt;&gt; (right shift)<\/td>\n<td>Shifts the bits right<br \/>\n        \u5411\u53f3\u79fb\u52a8\u4f4d<\/td>\n<td>0110 &gt;&gt; 1 = 0011 (3)<\/td>\n<\/tr>\n<\/table>\n<p>To show these in action, create a new Console Application project named FunWithBitwiseOperations.<br \/>\n\u82e5\u8981\u5728\u64cd\u4f5c\u4e2d\u663e\u793a\u8fd9\u4e9b\u5185\u5bb9\uff0c\u8bf7\u521b\u5efa\u4e00\u4e2a\u540d\u4e3a FunWithBitwiseOperations\u7684\u65b0\u63a7\u5236\u53f0\u5e94\u7528\u7a0b\u5e8f\u9879\u76ee\u3002<\/p>\n<p>Update the Program.cs file to the following code:<br \/>\n\u5c06\u7a0b\u5e8f.cs\u6587\u4ef6\u66f4\u65b0\u4e3a\u4ee5\u4e0b\u4ee3\u7801\uff1a<\/p>\n<p>using FunWithBitwiseOperations; Console.WriteLine(&quot;===== Fun wih Bitwise Operations&quot;);<br \/>\nConsole.WriteLine(&quot;6 &amp; 4 = {0} | {1}&quot;, 6 &amp; 4, Convert.ToString((6 &amp; 4),2));<br \/>\nConsole.WriteLine(&quot;6 | 4 = {0} | {1}&quot;, 6 | 4, Convert.ToString((6 | 4),2));<br \/>\nConsole.WriteLine(&quot;6 ^ 4 = {0} | {1}&quot;, 6 ^ 4, Convert.ToString((6 ^ 4),2));<br \/>\nConsole.WriteLine(&quot;6 &lt;&lt; 1 = {0} | {1}&quot;, 6 &lt;&lt; 1, Convert.ToString((6 &lt;&lt; 1),2));<br \/>\nConsole.WriteLine(&quot;6 &gt;&gt; 1 = {0} | {1}&quot;, 6 &gt;&gt; 1, Convert.ToString((6 &gt;&gt; 1),2)); Console.WriteLine(&quot;~6 = {0} | {1}&quot;, ~6, Convert.ToString(~((short)6),2)); Console.WriteLine(&quot;Int.MaxValue {0}&quot;, Convert.ToString((int.MaxValue),2)); Console.readLine();<\/p>\n<p>When you execute the code, you will see the following result:<br \/>\n\u6267\u884c\u4ee3\u7801\u65f6\uff0c\u60a8\u5c06\u770b\u5230\u4ee5\u4e0b\u7ed3\u679c\uff1a<\/p>\n<p>===== Fun wih Bitwise Operations 6 &amp; 4 = 4 | 100<br \/>\n6 | 4 = 6 | 110<br \/>\n6 ^ 4 = 2 | 10<br \/>\n6 &lt;&lt; 1 = 12 | 1100<br \/>\n6 &gt;&gt; 1 = 3 | 11<br \/>\n~6 = -7 | 11111111111111111111111111111001<br \/>\nInt.MaxValue 1111111111111111111111111111111<\/p>\n<p>Now that you know the basics of bitwise operations, it is time to apply them to enums. Add a new file named ContactPreferenceEnum.cs and update the code to the following:<br \/>\n\u73b0\u5728\u60a8\u5df2\u7ecf\u4e86\u89e3\u4e86\u6309\u4f4d\u8fd0\u7b97\u7684\u57fa\u7840\u77e5\u8bc6\uff0c\u662f\u65f6\u5019\u5c06\u5b83\u4eec\u5e94\u7528\u4e8e\u679a\u4e3e\u4e86\u3002\u6dfb\u52a0\u4e00\u4e2a\u540d\u4e3a ContactPreferenceEnum.cs \u7684\u65b0\u6587\u4ef6\uff0c\u5e76\u5c06\u4ee3\u7801\u66f4\u65b0\u4e3a\u4ee5\u4e0b\u5185\u5bb9\uff1a<\/p>\n<p>namespace FunWithBitwiseOperations<br \/>\n{<br \/>\n[Flags]<br \/>\npublic enum ContactPreferenceEnum<br \/>\n{<br \/>\nNone = 1,<br \/>\nEmail = 2,<br \/>\nPhone = 4,<br \/>\nPonyexpress = 6<br \/>\n}<br \/>\n}<\/p>\n<p>Notice the Flags attribute. This allows multiple values from an enum to be combined into a single variable. For example, Email and Phone can be combined like this:<br \/>\n\u8bf7\u6ce8\u610f\u201c\u6807\u5fd7\u201d\u5c5e\u6027\u3002\u8fd9\u5141\u8bb8\u5c06\u679a\u4e3e\u4e2d\u7684\u591a\u4e2a\u503c\u7ec4\u5408\u5230\u5355\u4e2a\u53d8\u91cf\u4e2d\u3002\u4f8b\u5982\uff0c\u7535\u5b50\u90ae\u4ef6\u548c\u7535\u8bdd\u53ef\u4ee5\u50cf\u8fd9\u6837\u7ec4\u5408\uff1a<\/p>\n<p>ContactPreferenceEnum emailAndPhone = ContactPreferenceEnum.Email | ContactPreferenceEnum.Phone;<\/p>\n<p>This allows you to check if one of the values exists in the combined value. For example, if you want to check to see which ContactPreference value is in emailAndPhone variable, you can use the following code:<br \/>\n\u8fd9\u5141\u8bb8\u60a8\u68c0\u67e5\u7ec4\u5408\u503c\u4e2d\u662f\u5426\u5b58\u5728\u5176\u4e2d\u4e00\u4e2a\u503c\u3002\u4f8b\u5982\uff0c\u5982\u679c\u8981\u68c0\u67e5\u7535\u5b50\u90ae\u4ef6\u548c\u7535\u8bdd\u53d8\u91cf\u4e2d\u7684\u54ea\u4e2a\u8054\u7cfb\u4eba\u9996\u9009\u9879\u503c\uff0c\u5219\u53ef\u4ee5\u4f7f\u7528\u4ee5\u4e0b\u4ee3\u7801\uff1a<\/p>\n<p>Console.WriteLine(&quot;None? {0}&quot;, (emailAndPhone | ContactPreferenceEnum.None) == emailAndPhone); Console.WriteLine(&quot;Email? {0}&quot;, (emailAndPhone | ContactPreferenceEnum.Email) == emailAndPhone); Console.WriteLine(&quot;Phone? {0}&quot;, (emailAndPhone | ContactPreferenceEnum.Phone) == emailAndPhone); Console.WriteLine(&quot;Text? {0}&quot;, (emailAndPhone | ContactPreferenceEnum.Text) == emailAndPhone);<\/p>\n<p>When executed, the following is presented to the console window:<br \/>\n\u6267\u884c\u65f6\uff0c\u5c06\u663e\u793a\u4ee5\u4e0b\u5185\u5bb9\u5230\u63a7\u5236\u53f0\u7a97\u53e3\uff1a<\/p>\n<p>None? False Email? True Phone? True Text? False<br \/>\n\u6ca1\u6709\uff1f\u865a\u5047\u7535\u5b50\u90ae\u4ef6\uff1f\u771f\u6b63\u7684\u7535\u8bdd\uff1f\u771f\u6587\uff1f\u5047<\/p>\n<h2>Understanding the Structure<\/h2>\n<p>\u4e86\u89e3\u7ed3\u6784<\/p>\n<p>Now that you understand the role of enumeration types, let\u2019s examine the use of .NET Core structures (or simply structs). Structure types are well suited for modeling mathematical, geometrical, and other \u201catomic\u201d entities in your application. A structure (such as an enumeration) is a user-defined type; however, structures are not simply a collection of name-value pairs. Rather, structures are types that can contain any number of data fields and members that operate on these fields.<br \/>\n\u73b0\u5728\uff0c\u4f60\u5df2\u4e86\u89e3\u679a\u4e3e\u7c7b\u578b\u7684\u4f5c\u7528\uff0c\u63a5\u4e0b\u6765\u8ba9\u6211\u4eec\u6765\u770b\u770b .NET Core \u7ed3\u6784\uff08\u6216\u7b80\u5355\u7684\u7ed3\u6784\uff09\u7684\u7528\u6cd5\u3002\u7ed3\u6784\u7c7b\u578b\u975e\u5e38\u9002\u5408\u5bf9\u5e94\u7528\u7a0b\u5e8f\u4e2d\u7684\u6570\u5b66\u3001\u51e0\u4f55\u548c\u5176\u4ed6\u201c\u539f\u5b50\u201d\u5b9e\u4f53\u8fdb\u884c\u5efa\u6a21\u3002\u7ed3\u6784\uff08\u5982\u679a\u4e3e\uff09\u662f\u7528\u6237\u5b9a\u4e49\u7684\u7c7b\u578b;\u4f46\u662f\uff0c\u7ed3\u6784\u4e0d\u4ec5\u4ec5\u662f\u540d\u79f0-\u503c\u5bf9\u7684\u96c6\u5408\u3002\u76f8\u53cd\uff0c\u7ed3\u6784\u662f\u53ef\u4ee5\u5305\u542b\u4efb\u610f\u6570\u91cf\u7684\u6570\u636e\u5b57\u6bb5\u548c\u5bf9\u8fd9\u4e9b\u5b57\u6bb5\u8fdb\u884c\u64cd\u4f5c\u7684\u6210\u5458\u7684\u7c7b\u578b\u3002<\/p>\n<p>\u25a0   Note  if you have a background in oop, you can think of a structure as a \u201clightweight class type,\u201d given that structures provide a way to define a type that supports encapsulation but cannot be used to build a family of related types. they can\u2019t inherit from other class or structure types and can\u2019t be the base of a class. inheritance is covered in Chapter 5. structures can implement interfaces, which are covered in Chapter 8. When you need to build a family of related types through inheritance, you will need to make use of class types.<br \/>\n\u8bf7\u6ce8\u610f\uff0c\u5982\u679c\u60a8\u6709 oop \u7684\u80cc\u666f\uff0c\u5219\u53ef\u4ee5\u5c06\u7ed3\u6784\u89c6\u4e3a\u201c\u8f7b\u91cf\u7ea7\u7c7b\u7c7b\u578b\u201d\uff0c\u56e0\u4e3a\u7ed3\u6784\u63d0\u4f9b\u4e86\u4e00\u79cd\u5b9a\u4e49\u652f\u6301\u5c01\u88c5\u4f46\u4e0d\u80fd\u7528\u4e8e\u6784\u5efa\u76f8\u5173\u7c7b\u578b\u65cf\u7684\u7c7b\u578b\u7684\u65b9\u6cd5\u3002 \u5b83\u4eec\u4e0d\u80fd\u4ece\u5176\u4ed6\u7c7b\u6216\u7ed3\u6784\u7c7b\u578b\u7ee7\u627f\uff0c\u4e5f\u4e0d\u80fd\u662f\u7c7b\u7684\u57fa\u3002 \u7ee7\u627f\u5728\u7b2c 5 \u7ae0\u4e2d\u4ecb\u7ecd\u3002 \u7ed3\u6784\u53ef\u4ee5\u5b9e\u73b0\u63a5\u53e3\uff0c \u7b2c8\u7ae0\u5bf9\u6b64\u8fdb\u884c\u4e86\u4ecb\u7ecd\u3002\u5f53\u60a8\u9700\u8981\u901a\u8fc7\u7ee7\u627f\u6784\u5efa\u76f8\u5173\u7c7b\u578b\u7684\u65cf\u65f6\uff0c\u60a8\u5c06\u9700\u8981\u4f7f\u7528\u7c7b\u7c7b\u578b\u3002<\/p>\n<p>On the surface, the process of defining and using structures is simple, but as they say, the devil is in the details. To begin understanding the basics of structure types, create a new project named FunWithStructures. In C#, structures are defined using the struct keyword. Define a new structure named Point, which defines two member variables of type int and a set of methods to interact with said data.<br \/>\n\u4ece\u8868\u9762\u4e0a\u770b\uff0c\u5b9a\u4e49\u548c\u4f7f\u7528\u7ed3\u6784\u7684\u8fc7\u7a0b\u5f88\u7b80\u5355\uff0c\u4f46\u6b63\u5982\u4ed6\u4eec\u6240\u8bf4\uff0c\u9b54\u9b3c\u5728\u7ec6\u8282\u4e2d\u3002\u82e5\u8981\u5f00\u59cb\u4e86\u89e3\u7ed3\u6784\u7c7b\u578b\u7684\u57fa\u7840\u77e5\u8bc6\uff0c\u8bf7\u521b\u5efa\u4e00\u4e2a\u540d\u4e3aFunWithStructures\u3002\u5728 C# \u4e2d\uff0c\u7ed3\u6784\u662f\u4f7f\u7528 struct \u5173\u952e\u5b57\u5b9a\u4e49\u7684\u3002\u5b9a\u4e49\u4e00\u4e2a\u540d\u4e3aPoint\uff0c\u5b83\u5b9a\u4e49\u4e86\u4e24\u4e2a int \u7c7b\u578b\u7684\u6210\u5458\u53d8\u91cf\u548c\u4e00\u7ec4\u4e0e\u6240\u8ff0\u6570\u636e\u4ea4\u4e92\u7684\u65b9\u6cd5\u3002<br \/>\nstruct Point<br \/>\n{<br \/>\n\/\/ Fields of the structure. public int X;<br \/>\npublic int Y;<\/p>\n<p>\/\/ Add 1 to the (X, Y) position. public void Increment()<\/p>\n<p>{<br \/>\nX++; Y++;<br \/>\n}<\/p>\n<p>\/\/ Subtract 1 from the (X, Y) position. public void Decrement()<br \/>\n{<br \/>\nX--; Y--;<br \/>\n}<\/p>\n<p>\/\/ Display the current position. public void Display()<br \/>\n{<br \/>\nConsole.WriteLine(&quot;X = {0}, Y = {1}&quot;, X, Y);<br \/>\n}<br \/>\n}<\/p>\n<p>Here, you have defined your two integer fields (X and Y) using the public keyword, which is an access control modifier (Chapter 5 continues this discussion). Declaring data with the public keyword ensures the caller has direct access to the data from a given Point variable (via the dot operator).<br \/>\n\u5728\u8fd9\u91cc\uff0c\u60a8\u4f7f\u7528 public \u5173\u952e\u5b57\u5b9a\u4e49\u4e86\u4e24\u4e2a\u6574\u6570\u5b57\u6bb5\uff08X \u548c Y\uff09\uff0c\u8fd9\u662f\u4e00\u4e2a\u8bbf\u95ee\u63a7\u5236\u4fee\u9970\u7b26\uff08\u7b2c 5 \u7ae0\u7ee7\u7eed\u8ba8\u8bba\uff09\u3002\u4f7f\u7528 public \u5173\u952e\u5b57\u58f0\u660e\u6570\u636e\u53ef\u786e\u4fdd\u8c03\u7528\u65b9\u53ef\u4ee5\u76f4\u63a5\u8bbf\u95ee\u6765\u81ea\u7ed9\u5b9a Point \u53d8\u91cf\u7684\u6570\u636e\uff08\u901a\u8fc7\u70b9\u8fd0\u7b97\u7b26\uff09\u3002<\/p>\n<p>\u25a0   Note  it is typically considered bad style to define public data within a class or structure. rather, you will want to define private data, which can be accessed and changed using public properties. these details will be examined in Chapter 5.<br \/>\n\u8bf7\u6ce8\u610f\uff0c\u5728\u7c7b\u6216\u7ed3\u6784\u4e2d\u5b9a\u4e49\u516c\u5171\u6570\u636e\u901a\u5e38\u88ab\u8ba4\u4e3a\u662f\u4e0d\u597d\u7684\u6837\u5f0f\u3002\u76f8\u53cd\uff0c\u60a8\u9700\u8981\u5b9a\u4e49\u79c1\u6709\u6570\u636e\uff0c\u53ef\u4ee5\u4f7f\u7528\u516c\u5171\u5c5e\u6027\u8bbf\u95ee\u548c\u66f4\u6539\u8fd9\u4e9b\u6570\u636e\u3002 \u8fd9\u4e9b\u7ec6\u8282\u5c06\u5728\u7b2c5\u7ae0\u4e2d\u7814\u7a76\u3002<\/p>\n<p>Here is code that takes the Point type out for a test-drive:<br \/>\n\u4e0b\u9762\u662f\u5c06 Point \u7c7b\u578b\u53d6\u51fa\u7528\u4e8e\u4f53\u9a8c\u7248\u7684\u4ee3\u7801\uff1a<\/p>\n<p>Console.WriteLine(&quot;<strong><strong><em> A First Look at Structures <\/em><\/strong><\/strong>\\n&quot;);<\/p>\n<p>\/\/ Create an initial Point. Point myPoint;<br \/>\nmyPoint.X = 349;<br \/>\nmyPoint.Y = 76; myPoint.Display();<\/p>\n<p>\/\/ Adjust the X and Y values. myPoint.Increment(); myPoint.Display(); Console.ReadLine();<\/p>\n<p>The output is as you would expect.<br \/>\n\u8f93\u51fa\u5982\u60a8\u6240\u6599\u3002<\/p>\n<p><strong><strong><em> A First Look at Structures <\/em><\/strong><\/strong> X = 349, Y = 76<br \/>\nX = 350, Y = 77<\/p>\n<p>There are some rules regarding structures. First, a structure can\u2019t inherit from other class or structure types and can\u2019t be the base of a class. Structures can implement interfaces.<br \/>\n\u6709\u4e00\u4e9b\u5173\u4e8e\u7ed3\u6784\u7684\u89c4\u5219\u3002\u9996\u5148\uff0c\u7ed3\u6784\u4e0d\u80fd\u4ece\u5176\u4ed6\u7c7b\u6216\u7ed3\u6784\u7c7b\u578b\u7ee7\u627f\uff0c\u4e5f\u4e0d\u80fd\u662f\u7c7b\u7684\u57fa\u3002\u7ed3\u6784\u53ef\u4ee5\u5b9e\u73b0\u63a5\u53e3\u3002<\/p>\n<h2>Creating Structure Variables<\/h2>\n<p>\u521b\u5efa\u7ed3\u6784\u53d8\u91cf<br \/>\nWhen you want to create a structure variable, you have a variety of options. Here, you simply create a Point variable and assign each piece of public field data before invoking its members. If you do not assign each piece of public field data (X and Y in this case) before using the structure, you will receive a compiler error.<br \/>\n\u5f53\u60a8\u60f3\u8981\u521b\u5efa\u7ed3\u6784\u53d8\u91cf\u65f6\uff0c\u6709\u591a\u79cd\u9009\u62e9\u3002\u5728\u8fd9\u91cc\uff0c\u60a8\u53ea\u9700\u521b\u5efa\u4e00\u4e2a Point \u53d8\u91cf\u5e76\u5728\u8c03\u7528\u5176\u6210\u5458\u4e4b\u524d\u5206\u914d\u6bcf\u6761\u516c\u5171\u5b57\u6bb5\u6570\u636e\u3002\u5982\u679c\u5728\u4f7f\u7528\u7ed3\u6784\u4e4b\u524d\u672a\u5206\u914d\u6bcf\u6761\u516c\u5171\u5b57\u6bb5\u6570\u636e\uff08\u5728\u672c\u4f8b\u4e2d\u4e3a X \u548c Y\uff09\uff0c\u60a8\u5c06\u6536\u5230\u7f16\u8bd1\u5668\u9519\u8bef\u3002<\/p>\n<p>\/\/ Error! Did not assign Y value. Point p1;<br \/>\np1.X = 10;<br \/>\np1.Display();<\/p>\n<p>\/\/ OK! Both fields assigned before use. Point p2;<br \/>\np2.X = 10;<br \/>\np2.Y = 10;<br \/>\np2.Display();<\/p>\n<p>As an alternative, you can create structure variables using the C# new keyword, which will invoke the structure\u2019s default constructor. By definition, a default constructor does not take any arguments. The benefit of invoking the default constructor of a structure is that each piece of field data is automatically set to its default value.<br \/>\n\u6216\u8005\uff0c\u53ef\u4ee5\u4f7f\u7528 C# new \u5173\u952e\u5b57\u521b\u5efa\u7ed3\u6784\u53d8\u91cf\uff0c\u8be5\u5173\u952e\u5b57\u5c06\u8c03\u7528\u7ed3\u6784\u7684\u9ed8\u8ba4\u6784\u9020\u51fd\u6570\u3002\u6839\u636e\u5b9a\u4e49\uff0c\u9ed8\u8ba4\u6784\u9020\u51fd\u6570\u4e0d\u63a5\u53d7\u4efb\u4f55\u53c2\u6570\u3002\u8c03\u7528\u7ed3\u6784\u7684\u9ed8\u8ba4\u6784\u9020\u51fd\u6570\u7684\u597d\u5904\u662f\uff0c\u6bcf\u6bb5\u5b57\u6bb5\u6570\u636e\u90fd\u81ea\u52a8\u8bbe\u7f6e\u4e3a\u5176\u9ed8\u8ba4\u503c\u3002<\/p>\n<p>\/\/ Set all fields to default values<br \/>\n\/\/ \u5c06\u6240\u6709\u5b57\u6bb5\u8bbe\u7f6e\u4e3a\u9ed8\u8ba4\u503c<br \/>\n\/\/ using the default constructor.<br \/>\n\/\/ \u4f7f\u7528\u9ed8\u8ba4\u6784\u9020\u51fd\u6570\u3002<br \/>\nPoint p1 = new Point();<\/p>\n<p>\/\/ Prints X=0,Y=0. p1.Display();<\/p>\n<h2>Structure Constructors (Updated 10.0)<\/h2>\n<p>\u7ed3\u6784\u6784\u9020\u51fd\u6570\uff0810.0 \u66f4\u65b0\uff09<\/p>\n<p>It is also possible to design a structure with a custom constructor. This allows you to specify the values of field data upon variable creation, rather than having to set each data member field by field. Chapter 5 will provide a detailed examination of constructors; however, to illustrate, update the Point structure with the following code:<br \/>\n\u4e5f\u53ef\u4ee5\u4f7f\u7528\u81ea\u5b9a\u4e49\u6784\u9020\u51fd\u6570\u8bbe\u8ba1\u7ed3\u6784\u3002\u8fd9\u5141\u8bb8\u60a8\u5728\u521b\u5efa\u53d8\u91cf\u65f6\u6307\u5b9a\u5b57\u6bb5\u6570\u636e\u7684\u503c\uff0c\u800c\u4e0d\u5fc5\u9010\u4e2a\u5b57\u6bb5\u8bbe\u7f6e\u6bcf\u4e2a\u6570\u636e\u6210\u5458\u3002\u7b2c5\u7ae0\u5c06\u63d0\u4f9b\u5bf9\u6784\u9020\u51fd\u6570\u7684\u8be6\u7ec6\u68c0\u67e5;\u4f46\u662f\uff0c\u4e3a\u4e86\u8bf4\u660e\uff0c\u8bf7\u4f7f\u7528\u4ee5\u4e0b\u4ee3\u7801\u66f4\u65b0 Point \u7ed3\u6784\uff1a<\/p>\n<p>struct Point<br \/>\n{<br \/>\n\/\/ Fields of the structure. public int X;<br \/>\npublic int Y;<\/p>\n<p>\/\/ A custom constructor.<br \/>\npublic Point(int xPos, int yPos)<br \/>\n{<\/p>\n<p>}<br \/>\n...<br \/>\n}<\/p>\n<p>X = xPos;<br \/>\nY = yPos;<\/p>\n<p>With this, you could now create Point variables, as follows:<br \/>\n\u6709\u4e86\u8fd9\u4e2a\uff0c\u60a8\u73b0\u5728\u53ef\u4ee5\u521b\u5efa\u70b9\u53d8\u91cf\uff0c\u5982\u4e0b\u6240\u793a\uff1a<\/p>\n<p>\/\/ Call custom constructor. Point p2 = new Point(50, 60);<\/p>\n<p>\/\/ Prints X=50,Y=60. p2.Display();<\/p>\n<p>Prior to C# 10, you could not declare a parameterless (i.e., default) constructor on a structure, as it was provided in the implementation of structure types. Now you can create Point variables, as follows:<br \/>\n\u5728 C# 10 \u4e4b\u524d\uff0c\u65e0\u6cd5\u5728\u7ed3\u6784\u4e0a\u58f0\u660e\u65e0\u53c2\u6570\uff08\u5373\u9ed8\u8ba4\uff09\u6784\u9020\u51fd\u6570\uff0c\u56e0\u4e3a\u5b83\u662f\u5728\u7ed3\u6784\u7c7b\u578b\u7684\u5b9e\u73b0\u4e2d\u63d0\u4f9b\u7684\u3002\u73b0\u5728\uff0c\u60a8\u53ef\u4ee5\u521b\u5efa\u70b9\u53d8\u91cf\uff0c\u5982\u4e0b\u6240\u793a\uff1a<\/p>\n<p>\/\/ Call custom constructor. Point p2 = new Point(50, 60);<\/p>\n<p>\/\/ Prints X=50,Y=60. p2.Display();<\/p>\n<p>Regardless of which constructor you choose to add, prior to C# 10, you could not declare a parameterless (i.e., default) constructor on a structure, as it was provided in the implementation of structure types. Now, this is possible, as long as all value types are assigned a value before the code in the constructor end. With this, you can now update the Point structure to the following:<br \/>\n\u65e0\u8bba\u9009\u62e9\u6dfb\u52a0\u54ea\u4e2a\u6784\u9020\u51fd\u6570\uff0c\u5728 C# 10 \u4e4b\u524d\uff0c\u90fd\u65e0\u6cd5\u5728\u7ed3\u6784\u4e0a\u58f0\u660e\u65e0\u53c2\u6570\uff08\u5373\u9ed8\u8ba4\uff09\u6784\u9020\u51fd\u6570\uff0c\u56e0\u4e3a\u5b83\u662f\u5728\u7ed3\u6784\u7c7b\u578b\u7684\u5b9e\u73b0\u4e2d\u63d0\u4f9b\u7684\u3002\u73b0\u5728\uff0c\u8fd9\u662f\u53ef\u80fd\u7684\uff0c\u53ea\u8981\u5728\u6784\u9020\u51fd\u6570\u4e2d\u7684\u4ee3\u7801\u7ed3\u675f\u4e4b\u524d\u4e3a\u6240\u6709\u503c\u7c7b\u578b\u5206\u914d\u4e00\u4e2a\u503c\u3002\u8fd9\u6837\uff0c\u60a8\u73b0\u5728\u53ef\u4ee5\u5c06\u70b9\u7ed3\u6784\u66f4\u65b0\u4e3a\u4ee5\u4e0b\u5185\u5bb9\uff1a<\/p>\n<p>struct Point<br \/>\n{<br \/>\n\/\/omitted for brevity<br \/>\n\/\/Parameterless constructor public Point()<br \/>\n{<br \/>\nX = 0;<br \/>\nY= 0;<br \/>\n}<br \/>\n\/\/ A custom constructor.<br \/>\npublic Point(int xPos, int yPos)<br \/>\n{<br \/>\nX = xPos;<br \/>\nY = yPos;<br \/>\n}<br \/>\n}<\/p>\n<p>\u25a0   Note  C# 10 and .net 6 introduce the record struct, which will be covered in Chapter 5.<br \/>\n\u6ce8\u610f C# 10 \u548c .net 6 \u4ecb\u7ecd\u4e86\u8bb0\u5f55\u7ed3\u6784\uff0c\u8fd9\u5c06\u5728\u7b2c 5 \u7ae0\u4e2d\u4ecb\u7ecd\u3002<\/p>\n<h2>Using Field Initializers (New 10.0)<\/h2>\n<p>\u4f7f\u7528\u5b57\u6bb5\u521d\u59cb\u503c\u8bbe\u5b9a\u9879\uff08\u65b0 10.0\uff09<\/p>\n<p>New in C# 10, structure fields can be initialized when declared. Update the code to the following, which initializes X with a value of 5, and Y with a value of 7:<br \/>\nC# 10 \u4e2d\u7684\u65b0\u589e\u529f\u80fd\u662f\uff0c\u7ed3\u6784\u5b57\u6bb5\u53ef\u4ee5\u5728\u58f0\u660e\u65f6\u521d\u59cb\u5316\u3002\u5c06\u4ee3\u7801\u66f4\u65b0\u4e3a\u4ee5\u4e0b\u5185\u5bb9\uff0c\u8be5\u4ee3\u7801\u4f7f\u7528\u503c 5 \u521d\u59cb\u5316 X\uff0c\u7528\u503c 7 \u521d\u59cb\u5316 Y\uff1a<\/p>\n<p>struct Point<br \/>\n{<br \/>\n\/\/ Fields of the structure. public int X = 5;<\/p>\n<p>public int Y = 7;<br \/>\n\/\/omitted for brevity<br \/>\n}<\/p>\n<p>With this update, the parameterless constructor no longer needs to initialize the X and Y fields:<br \/>\n\u901a\u8fc7\u6b64\u66f4\u65b0\uff0c\u65e0\u53c2\u6570\u6784\u9020\u51fd\u6570\u4e0d\u518d\u9700\u8981\u521d\u59cb\u5316 X \u548c Y \u5b57\u6bb5\uff1a<\/p>\n<p>struct Point<br \/>\n{<br \/>\n\/\/omitted for brevity<br \/>\n\/\/Parameterless constructor public Point() { }<br \/>\n\/\/omitted for brevity<br \/>\n}<\/p>\n<h2>Using Read-Only Structs (New 7.2)<\/h2>\n<p>\u4f7f\u7528\u53ea\u8bfb\u7ed3\u6784\uff08\u65b0 7.2\uff09<\/p>\n<p>Structs can also be marked as read-only if there is a need for them to be immutable. Immutable objects must be set up at construction and because they cannot be changed, can be more performant. When declaring a struct as read-only, all the properties must also be read-only. But you might ask, how can a property be set (as all properties must be on a struct) if it is read-only? The answer is that the value must be set during the construction of the struct.<br \/>\n\u5982\u679c\u9700\u8981\u7ed3\u6784\u4e0d\u53ef\u53d8\uff0c\u4e5f\u53ef\u4ee5\u5c06\u5176\u6807\u8bb0\u4e3a\u53ea\u8bfb\u3002\u4e0d\u53ef\u53d8\u5bf9\u8c61\u5fc5\u987b\u5728\u6784\u9020\u65f6\u8bbe\u7f6e\uff0c\u5e76\u4e14\u7531\u4e8e\u5b83\u4eec\u65e0\u6cd5\u66f4\u6539\uff0c\u56e0\u6b64\u53ef\u4ee5\u63d0\u9ad8\u6027\u80fd\u3002\u5c06\u7ed3\u6784\u58f0\u660e\u4e3a\u53ea\u8bfb\u65f6\uff0c\u6240\u6709\u5c5e\u6027\u4e5f\u5fc5\u987b\u662f\u53ea\u8bfb\u7684\u3002\u4f46\u4f60\u53ef\u80fd\u4f1a\u95ee\uff0c\u5982\u679c\u5c5e\u6027\u662f\u53ea\u8bfb\u7684\uff0c\u5982\u4f55\u8bbe\u7f6e\u5c5e\u6027\uff08\u56e0\u4e3a\u6240\u6709\u5c5e\u6027\u90fd\u5fc5\u987b\u5728\u7ed3\u6784\u4e0a\uff09\uff1f\u7b54\u6848\u662f\u5fc5\u987b\u5728\u7ed3\u6784\u6784\u9020\u671f\u95f4\u8bbe\u7f6e\u8be5\u503c\u3002<\/p>\n<p>Update the point class to the following example:<br \/>\n\u5c06\u70b9\u7c7b\u66f4\u65b0\u4e3a\u4ee5\u4e0b\u793a\u4f8b\uff1a<\/p>\n<p>readonly struct ReadOnlyPoint<br \/>\n{<br \/>\n\/\/ Fields of the structure. public int X {get; }<br \/>\npublic int Y { get; }<\/p>\n<p>\/\/ Display the current position and name. public void Display()<br \/>\n{<br \/>\nConsole.WriteLine($&quot;X = {X}, Y = {Y}&quot;);<br \/>\n}<\/p>\n<p>public ReadOnlyPoint(int xPos, int yPos)<br \/>\n{<br \/>\nX = xPos;<br \/>\nY = yPos;<br \/>\n}<br \/>\n}<\/p>\n<p>The Increment and Decrement methods have been removed since the variables are read-only. Notice also the two properties, X and Y. Instead of setting them up as fields, they are created as read-only automatic properties. Automatic properties are covered in Chapter 5.<br \/>\n\u589e\u91cf\u548c\u9012\u51cf\u65b9\u6cd5\u5df2\u88ab\u5220\u9664\uff0c\u56e0\u4e3a\u8fd9\u4e9b\u53d8\u91cf\u662f\u53ea\u8bfb\u7684\u3002\u53e6\u8bf7\u6ce8\u610f\u4e24\u4e2a\u5c5e\u6027 X \u548c Y\u3002\u5b83\u4eec\u4e0d\u662f\u8bbe\u7f6e\u4e3a\u5b57\u6bb5\uff0c\u800c\u662f\u521b\u5efa\u4e3a\u53ea\u8bfb\u81ea\u52a8\u5c5e\u6027\u3002\u81ea\u52a8\u5c5e\u6027\u5728\u7b2c 5 \u7ae0\u4e2d\u4ecb\u7ecd\u3002<\/p>\n<h2>Using Read-Only Members (New 8.0)<\/h2>\n<p>\u4f7f\u7528\u53ea\u8bfb\u6210\u5458\uff08\u65b0\u7248 8.0\uff09<\/p>\n<p>New in C# 8.0, you can declare individual fields of a struct as readonly. This is more granular than making the entire struct read-only. The readonly modifier can be applied to methods, properties, and property accessors. Add the following struct code to your file, outside of the Program.cs file:<br \/>\n\u4f5c\u4e3a C# 8.0 \u4e2d\u7684\u65b0\u589e\u529f\u80fd\uff0c\u53ef\u4ee5\u5c06\u7ed3\u6784\u7684\u5404\u4e2a\u5b57\u6bb5\u58f0\u660e\u4e3a\u53ea\u8bfb\u3002\u8fd9\u6bd4\u5c06\u6574\u4e2a\u7ed3\u6784\u8bbe\u4e3a\u53ea\u8bfb\u66f4\u7cbe\u7ec6\u3002\u53ea\u8bfb\u4fee\u9970\u7b26\u53ef\u4ee5\u5e94\u7528\u4e8e\u65b9\u6cd5\u3001\u5c5e\u6027\u548c\u5c5e\u6027\u8bbf\u95ee\u5668\u3002\u5c06\u4ee5\u4e0b\u7ed3\u6784\u4ee3\u7801\u6dfb\u52a0\u5230\u7a0b\u5e8f.cs\u6587\u4ef6\u4e4b\u5916\u7684\u6587\u4ef6\uff1a<\/p>\n<p>struct PointWithReadOnly<br \/>\n{<br \/>\n\/\/ Fields of the structure. public int X;<br \/>\npublic readonly int Y; public readonly string Name;<\/p>\n<p>\/\/ Display the current position and name. public readonly void Display()<br \/>\n{<br \/>\nConsole.WriteLine($&quot;X = {X}, Y = {Y}, Name = {Name}&quot;);<br \/>\n}<\/p>\n<p>\/\/ A custom constructor.<br \/>\npublic PointWithReadOnly(int xPos, int yPos, string name)<br \/>\n{<br \/>\nX = xPos;<br \/>\nY = yPos;<br \/>\nName = name;<br \/>\n}<br \/>\n}<\/p>\n<p>To use this new struct, add the following to the top-level statements:<br \/>\n\u82e5\u8981\u4f7f\u7528\u6b64\u65b0\u7ed3\u6784\uff0c\u8bf7\u5c06\u4ee5\u4e0b\u5185\u5bb9\u6dfb\u52a0\u5230\u9876\u7ea7\u8bed\u53e5\u4e2d\uff1a<\/p>\n<p>PointWithReadOnly p3 =<br \/>\nnew PointWithReadOnly(50,60,&quot;Point w\/RO&quot;); p3.Display();<\/p>\n<h2>Using ref Structs (New 7.2)<\/h2>\n<p>\u4f7f\u7528 ref \u7ed3\u6784\uff08\u65b0 7.2\uff09<\/p>\n<p>Also added in C# 7.2, the ref modifier can be used when defining a struct. This requires all instances of the struct to be stack allocated and cannot be assigned as a property of another class. The technical reason for this is that ref structs cannot referenced from the heap. The difference between the stack and the heap is covered in the next section.<br \/>\n\u540c\u6837\u5728 C# 7.2 \u4e2d\u6dfb\u52a0\u4e86 ref \u4fee\u9970\u7b26\uff0c\u53ef\u4ee5\u5728\u5b9a\u4e49\u7ed3\u6784\u65f6\u4f7f\u7528\u3002\u8fd9\u8981\u6c42\u5bf9\u7ed3\u6784\u7684\u6240\u6709\u5b9e\u4f8b\u8fdb\u884c\u5806\u6808\u5206\u914d\uff0c\u5e76\u4e14\u4e0d\u80fd\u5c06\u5176\u5206\u914d\u4e3a\u53e6\u4e00\u4e2a\u7c7b\u7684\u5c5e\u6027\u3002\u8fd9\u6837\u505a\u7684\u6280\u672f\u539f\u56e0\u662f ref \u7ed3\u6784\u4e0d\u80fd\u4ece\u5806\u4e2d\u5f15\u7528\u3002\u5806\u6808\u548c\u5806\u4e4b\u95f4\u7684\u533a\u522b\u5c06\u5728\u4e0b\u4e00\u8282\u4e2d\u4ecb\u7ecd\u3002<\/p>\n<p>These are some additional limitations of ref structs:<br \/>\n\u8fd9\u4e9b\u662f ref \u7ed3\u6784\u7684\u4e00\u4e9b\u9644\u52a0\u9650\u5236\uff1a<br \/>\n\u2022   They cannot be assigned to a variable of type object or dynamic, and they cannot be an interface type.<br \/>\n\u5b83\u4eec\u4e0d\u80fd\u5206\u914d\u7ed9\u5bf9\u8c61\u6216\u52a8\u6001\u7c7b\u578b\u7684\u53d8\u91cf\uff0c\u4e5f\u4e0d\u80fd\u662f\u63a5\u53e3\u7c7b\u578b\u3002<br \/>\n\u2022   They cannot implement interfaces.<br \/>\n\u5b83\u4eec\u65e0\u6cd5\u5b9e\u73b0\u63a5\u53e3\u3002<br \/>\n\u2022   They cannot be used as a property of a non-ref struct.<br \/>\n\u5b83\u4eec\u4e0d\u80fd\u7528\u4f5c\u975e\u5f15\u7528\u7ed3\u6784\u7684\u5c5e\u6027\u3002<br \/>\n\u2022   They cannot be used in async methods, iterators, lambda expressions, or local functions.<br \/>\n\u5b83\u4eec\u4e0d\u80fd\u5728\u5f02\u6b65\u65b9\u6cd5\u3001\u8fed\u4ee3\u5668\u3001lambda \u8868\u8fbe\u5f0f\u6216\u672c\u5730\u51fd\u6570\u4e2d\u4f7f\u7528\u3002<\/p>\n<p>The following code, which creates a simple struct and then attempts to create a property in that struct typed to a ref struct, will not compile:<br \/>\n\u4e0b\u9762\u7684\u4ee3\u7801\u521b\u5efa\u4e00\u4e2a\u7b80\u5355\u7684\u7ed3\u6784\uff0c\u7136\u540e\u5c1d\u8bd5\u5728\u8be5\u7ed3\u6784\u4e2d\u521b\u5efa\u4e00\u4e2a\u5c5e\u6027\uff0c\u952e\u5165\u4e3a ref \u7ed3\u6784\uff0c\u4f46\u4e0d\u4f1a\u7f16\u8bd1\uff1a<\/p>\n<p>struct NormalPoint<br \/>\n{<br \/>\n\/\/This does not compile<br \/>\npublic PointWithRef PropPointer { get; set; }<br \/>\n}<\/p>\n<p>Note that the readonly and ref modifiers can be combined to gain the benefits and restrictions of both.<br \/>\n\u8bf7\u6ce8\u610f\uff0c\u53ea\u8bfb\u548c\u5f15\u7528\u4fee\u9970\u7b26\u53ef\u4ee5\u7ec4\u5408\u5728\u4e00\u8d77\u4ee5\u83b7\u5f97\u4e24\u8005\u7684\u4f18\u70b9\u548c\u9650\u5236\u3002<\/p>\n<h3>Using Disposable ref Structs (New 8.0)<\/h3>\n<p>\u4f7f\u7528\u4e00\u6b21\u6027\u5f15\u7528\u7ed3\u6784\uff08\u65b0\u7248 8.0\uff09<\/p>\n<p>As covered in the previous section, ref structs (and read-only ref structs) cannot implement an interface and therefore cannot implement IDisposable. New in C# 8.0, ref structs and read-only ref structs can be made disposable by adding a public void Dispose() method.<br \/>\n\u5982\u4e0a\u4e00\u8282\u6240\u8ff0\uff0cref \u7ed3\u6784 \uff08\u548c\u53ea\u8bfb ref \u7ed3\u6784\uff09\u65e0\u6cd5\u5b9e\u73b0\u63a5\u53e3\uff0c\u56e0\u6b64\u65e0\u6cd5\u5b9e\u73b0 IDisposable\u3002C# 8.0 \u4e2d\u7684\u65b0\u529f\u80fd\u662f \uff0cref \u7ed3\u6784\u548c\u53ea\u8bfb ref \u7ed3\u6784\u53ef\u4ee5\u901a\u8fc7\u6dfb\u52a0\u516c\u5171 void Dispose\uff08\uff09 \u65b9\u6cd5\u4e00\u6b21\u6027\u4f7f\u7528\u3002<\/p>\n<p>Add the following struct definition to the Program.cs file:<br \/>\n\u5c06\u4ee5\u4e0b\u7ed3\u6784\u5b9a\u4e49\u6dfb\u52a0\u5230\u7a0b\u5e8f.cs\u6587\u4ef6\u4e2d\uff1a<\/p>\n<p>ref struct DisposableRefStruct<br \/>\n{<br \/>\npublic int X;<br \/>\npublic readonly int Y;<br \/>\npublic readonly void Display()<br \/>\n{<br \/>\nConsole.WriteLine($&quot;X = {X}, Y = {Y}&quot;);<br \/>\n}<br \/>\n\/\/ A custom constructor.<br \/>\npublic DisposableRefStruct(int xPos, int yPos)<br \/>\n{<br \/>\nX = xPos;<br \/>\nY = yPos;<br \/>\nConsole.WriteLine(&quot;Created!&quot;);<br \/>\n}<br \/>\npublic void Dispose()<br \/>\n{<br \/>\n\/\/clean up any resources here Console.WriteLine(&quot;Disposed!&quot;);<br \/>\n}<br \/>\n}<\/p>\n<p>Next, add the following to the end of the top-level statements to create and dispose of the new struct:<br \/>\n\u63a5\u4e0b\u6765\uff0c\u5c06\u4ee5\u4e0b\u5185\u5bb9\u6dfb\u52a0\u5230\u9876\u7ea7\u8bed\u53e5\u7684\u672b\u5c3e\uff0c\u4ee5\u521b\u5efa\u548c\u91ca\u653e\u65b0\u7ed3\u6784\uff1a<\/p>\n<p>var s = new DisposableRefStruct(50, 60); s.Display();<br \/>\ns.Dispose();<\/p>\n<p>\u25a0   Note object lifetime and disposing of objects are covered in depth in Chapter 9.<br \/>\n\u6ce8\u610f \u5bf9\u8c61\u751f\u5b58\u671f\u548c\u5bf9\u8c61\u7684\u5904\u7f6e\u5728\u7b2c 9 \u7ae0\u4e2d\u6709\u6df1\u5165\u4ecb\u7ecd\u3002<\/p>\n<p>To deepen your understanding of stack and heap allocation, you need to explore the distinction between a .NET Core value type and a .NET Core reference type.<br \/>\n\u82e5\u8981\u52a0\u6df1\u5bf9\u5806\u6808\u548c\u5806\u5206\u914d\u7684\u7406\u89e3\uff0c\u9700\u8981\u63a2\u7d22 .NET Core \u503c\u7c7b\u578b\u548c .NET Core \u5f15\u7528\u7c7b\u578b\u4e4b\u95f4\u7684\u533a\u522b\u3002<\/p>\n<h2>Understanding Value Types and Reference Types<\/h2>\n<p>\u25a0   Note the following discussion of value types and reference types assumes that you have a background in object-oriented programming. if this is not the case, you might want to skip to the \u201cunderstanding C# nullable types\u201d section of this chapter and return to this section after you have read Chapters 5 and 6.<br \/>\n\u8bf7\u6ce8\u610f\uff0c\u4ee5\u4e0b\u5173\u4e8e\u503c\u7c7b\u578b\u548c\u5f15\u7528\u7c7b\u578b\u7684\u8ba8\u8bba\u5047\u5b9a\u60a8\u5177\u6709\u9762\u5411\u5bf9\u8c61\u7f16\u7a0b\u7684\u80cc\u666f\u3002 \u5982\u679c\u4e0d\u662f\u8fd9\u79cd\u60c5\u51b5\uff0c\u60a8\u53ef\u80fd\u9700\u8981\u8df3\u5230\u672c\u7ae0\u7684\u201c\u4e86\u89e3 C# \u53ef\u4e3a null \u7684\u7c7b\u578b\u201d\u90e8\u5206\uff0c\u5e76\u5728\u9605\u8bfb\u7b2c 5 \u7ae0\u548c\u7b2c 6 \u7ae0\u540e\u8fd4\u56de\u5230\u672c\u8282\u3002<\/p>\n<p>Unlike arrays, strings, or enumerations, C# structures do not have an identically named representation in the .NET Core library (i.e., there is no System.Structure class) but are implicitly derived from System. ValueType. The role of System.ValueType is to ensure that the derived type (e.g., any structure) is allocated on the stack, rather than the garbage-collected heap. Simply put, data allocated on the stack can be created and destroyed quickly, as its lifetime is determined by the defining scope. Heap-allocated data, on the other hand, is monitored by the .NET Core garbage collector and has a lifetime that is determined by many factors, which will be examined in Chapter 9.<br \/>\n\u4e0e\u6570\u7ec4\u3001\u5b57\u7b26\u4e32\u6216\u679a\u4e3e\u4e0d\u540c\uff0cC# \u7ed3\u6784\u5728 .NET Core \u5e93\u4e2d\u6ca1\u6709\u540c\u540d\u7684\u8868\u793a\u5f62\u5f0f\uff08\u5373\uff0c\u6ca1\u6709 System.Structure \u7c7b\uff09\uff0c\u800c\u662f\u9690\u5f0f\u6d3e\u751f\u81ea System\u3002\u503c\u7c7b\u578b\u3002System.ValueType \u7684\u4f5c\u7528\u662f\u786e\u4fdd\u6d3e\u751f\u7c7b\u578b\uff08\u4f8b\u5982\uff0c\u4efb\u4f55\u7ed3\u6784\uff09\u5728\u5806\u6808\u4e0a\u5206\u914d\uff0c\u800c\u4e0d\u662f\u5728\u5783\u573e\u56de\u6536\u5806\u4e0a\u5206\u914d\u3002\u7b80\u800c\u8a00\u4e4b\uff0c\u5806\u6808\u4e0a\u5206\u914d\u7684\u6570\u636e\u53ef\u4ee5\u5feb\u901f\u521b\u5efa\u548c\u9500\u6bc1\uff0c\u56e0\u4e3a\u5176\u751f\u547d\u5468\u671f\u7531\u5b9a\u4e49\u8303\u56f4\u51b3\u5b9a\u3002\u53e6\u4e00\u65b9\u9762\uff0c\u5806\u5206\u914d\u7684\u6570\u636e\u7531 .NET Core \u5783\u573e\u56de\u6536\u5668\u76d1\u89c6\uff0c\u5176\u751f\u5b58\u671f\u7531\u8bb8\u591a\u56e0\u7d20\u51b3\u5b9a\uff0c\u8fd9\u4e9b\u56e0\u7d20\u5c06\u5728\u7b2c 9 \u7ae0\u4e2d\u4ecb\u7ecd\u3002<\/p>\n<p>Functionally, the only purpose of System.ValueType is to override the virtual methods defined by System.Object to use value-based versus reference-based semantics. As you might know, overriding is the process of changing the implementation of a virtual (or possibly abstract) method defined within a base class. The base class of ValueType is System.Object. In fact, the instance methods defined by System.<br \/>\n\u4ece\u529f\u80fd\u4e0a\u8bb2\uff0cSystem.ValueType \u7684\u552f\u4e00\u7528\u9014\u662f\u8986\u76d6 System.Object \u5b9a\u4e49\u7684\u865a\u62df\u65b9\u6cd5\uff0c\u4ee5\u4f7f\u7528\u57fa\u4e8e\u503c\u7684\u8bed\u4e49\u4e0e\u57fa\u4e8e\u5f15\u7528\u7684\u8bed\u4e49\u3002\u60a8\u53ef\u80fd\u77e5\u9053\uff0c\u91cd\u5199\u662f\u66f4\u6539\u57fa\u7c7b\u4e2d\u5b9a\u4e49\u7684\u865a\u62df\uff08\u6216\u53ef\u80fd\u662f\u62bd\u8c61\uff09\u65b9\u6cd5\u7684\u5b9e\u73b0\u7684\u8fc7\u7a0b\u3002ValueType \u7684\u57fa\u7c7b\u662f System.Object\u3002\u5b9e\u9645\u4e0a\uff0c\u5b9e\u4f8b\u65b9\u6cd5\u7531\u7cfb\u7edf\u5b9a\u4e49\u3002<\/p>\n<p>ValueType are identical to those of System.Object.<br \/>\nValueType \u4e0e System.Object \u76f8\u540c\u3002<\/p>\n<p>\/\/ Structures and enumerations implicitly extend System.ValueType. public abstract class ValueType : object<br \/>\n{<br \/>\npublic virtual bool Equals(object obj); public virtual int GetHashCode(); public Type GetType();<br \/>\npublic virtual string ToString();<br \/>\n}<\/p>\n<p>Given that value types are using value-based semantics, the lifetime of a structure (which includes all numerical data types [int, float], as well as any enum or structure) is predictable. When a structure variable falls out of the defining scope, it is removed from memory immediately.<br \/>\n\u9274\u4e8e\u503c\u7c7b\u578b\u4f7f\u7528\u57fa\u4e8e\u503c\u7684\u8bed\u4e49\uff0c\u7ed3\u6784\uff08\u5305\u62ec\u6240\u6709\u6570\u503c\u6570\u636e\u7c7b\u578b [int\u3001float] \u4ee5\u53ca\u4efb\u4f55\u679a\u4e3e\u6216\u7ed3\u6784\uff09\u7684\u751f\u5b58\u671f\u662f\u53ef\u9884\u6d4b\u7684\u3002\u5f53\u7ed3\u6784\u53d8\u91cf\u8d85\u51fa\u5b9a\u4e49\u8303\u56f4\u65f6\uff0c\u4f1a\u7acb\u5373\u5c06\u5176\u4ece\u5185\u5b58\u4e2d\u5220\u9664\u3002<\/p>\n<p>\/\/ Local structures are popped off<br \/>\n\/\/ \u672c\u5730\u7ed3\u6784\u88ab\u5f39\u51fa<br \/>\n\/\/ the stack when a method returns.<br \/>\n\/\/ \u65b9\u6cd5\u8fd4\u56de\u65f6\u7684\u5806\u6808\u3002<br \/>\nstatic void LocalValueTypes()<br \/>\n{<br \/>\n\/\/ Recall! &quot;int&quot; is really a System.Int32 structure. int i = 0;<\/p>\n<p>\/\/ Recall! Point is a structure type. Point p = new Point();<br \/>\n} \/\/ &quot;i&quot; and &quot;p&quot; popped off the stack here!<\/p>\n<h3>Using Value Types, Reference Types, and the Assignment Operator<\/h3>\n<p>\u4f7f\u7528\u503c\u7c7b\u578b\u3001\u5f15\u7528\u7c7b\u578b\u548c\u8d4b\u503c\u8fd0\u7b97\u7b26<\/p>\n<p>When you assign one value type to another, a member-by-member copy of the field data is achieved. In the case of a simple data type such as System.Int32, the only member to copy is the numerical value. However, in the case of your Point, the X and Y values are copied into the new structure variable. To illustrate,<br \/>\ncreate a new Console Application project named FunWithValueAndReferenceTypes and then copy your previous Point definition into your new namespace. Next, add the following local function to your top-level statements:<br \/>\n\u5c06\u4e00\u79cd\u503c\u7c7b\u578b\u5206\u914d\u7ed9\u53e6\u4e00\u79cd\u503c\u7c7b\u578b\u65f6\uff0c\u5c06\u5b9e\u73b0\u5b57\u6bb5\u6570\u636e\u7684\u6210\u5458\u526f\u672c\u3002\u5bf9\u4e8e\u7b80\u5355\u6570\u636e\u7c7b\u578b\uff08\u5982 System.Int32\uff09\uff0c\u552f\u4e00\u8981\u590d\u5236\u7684\u6210\u5458\u662f\u6570\u503c\u3002\u4f46\u662f\uff0c\u5bf9\u4e8e\u70b9\uff0cX \u548c Y \u503c\u5c06\u590d\u5236\u5230\u65b0\u7684\u7ed3\u6784\u53d8\u91cf\u4e2d\u3002\u4e3a\u4e86\u8bf4\u660e\uff0c\u521b\u5efa\u4e00\u4e2a\u540d\u4e3a FunWithValueAndReferenceType \u7684\u65b0\u63a7\u5236\u53f0\u5e94\u7528\u7a0b\u5e8f\u9879\u76ee\uff0c\u7136\u540e\u5c06\u4ee5\u524d\u7684 Point \u5b9a\u4e49\u590d\u5236\u5230\u65b0\u547d\u540d\u7a7a\u95f4\u4e2d\u3002\u63a5\u4e0b\u6765\uff0c\u5c06\u4ee5\u4e0b\u672c\u5730\u51fd\u6570\u6dfb\u52a0\u5230\u9876\u7ea7\u8bed\u53e5\u4e2d\uff1a<\/p>\n<p>\/\/ Assigning two intrinsic value types results in<br \/>\n\/\/ \u5206\u914d\u4e24\u4e2a\u5185\u90e8\u503c\u7c7b\u578b\u4f1a\u5bfc\u81f4<br \/>\n\/\/ two independent variables on the stack.<br \/>\n\/\/ \u5806\u6808\u4e0a\u7684\u4e24\u4e2a\u81ea\u53d8\u91cf\u3002<br \/>\nstatic void ValueTypeAssignment()<br \/>\n{<br \/>\nConsole.WriteLine(&quot;Assigning value types\\n&quot;);<\/p>\n<p>Point p1 = new Point(10, 10); Point p2 = p1;<\/p>\n<p>\/\/ Print both points. p1.Display();<br \/>\np2.Display();<\/p>\n<p>\/\/ Change p1.X and print again. p2.X is not changed. p1.X = 100;<br \/>\nConsole.WriteLine(&quot;\\n=&gt; Changed p1.X\\n&quot;); p1.Display();<br \/>\np2.Display();<br \/>\n}<\/p>\n<p>Here, you have created a variable of type Point (named p1) that is then assigned to another Point (p2).<br \/>\n\u5728\u8fd9\u91cc\uff0c\u60a8\u521b\u5efa\u4e86\u4e00\u4e2a\u7c7b\u578b\u4e3a Point\uff08\u540d\u4e3a p1\uff09\u7684\u53d8\u91cf\uff0c\u7136\u540e\u5c06\u5176\u5206\u914d\u7ed9\u53e6\u4e00\u4e2a Point \uff08p2\uff09\u3002<\/p>\n<p>Because Point is a value type, you have two copies of the Point type on the stack, each of which can be independently manipulated. Therefore, when you change the value of p1.X, the value of p2.X is unaffected.<br \/>\n\u7531\u4e8e Point \u662f\u503c\u7c7b\u578b\uff0c\u56e0\u6b64\u5806\u6808\u4e0a\u6709\u4e24\u4e2a Point \u7c7b\u578b\u7684\u526f\u672c\uff0c\u6bcf\u4e2a\u526f\u672c\u90fd\u53ef\u4ee5\u72ec\u7acb\u64cd\u4f5c\u3002\u56e0\u6b64\uff0c\u5f53\u60a8\u66f4\u6539 p1 \u7684\u503c\u65f6\u3002X\uff0cp2\u7684\u503c\u3002X \u4e0d\u53d7\u5f71\u54cd\u3002<\/p>\n<p>Assigning value types X = 10, Y = 10<br \/>\nX = 10, Y = 10<br \/>\n=&gt; Changed p1.X X = 100, Y = 10<br \/>\nX = 10, Y = 10<\/p>\n<p>In stark contrast to value types, when you apply the assignment operator to reference types (meaning all class instances), you are redirecting what the reference variable points to in memory. To illustrate, create a new class type named PointRef that has the same members as the Point structures, beyond renaming the constructor to match the class name.<br \/>\n\u4e0e\u503c\u7c7b\u578b\u5f62\u6210\u9c9c\u660e\u5bf9\u6bd4\u7684\u662f\uff0c\u5c06\u8d4b\u503c\u8fd0\u7b97\u7b26\u5e94\u7528\u4e8e\u5f15\u7528\u7c7b\u578b\uff08\u5373\u6240\u6709\u7c7b\u5b9e\u4f8b\uff09\u65f6\uff0c\u5c06\u5f15\u7528\u53d8\u91cf\u6307\u5411\u5185\u5b58\u4e2d\u7684\u5185\u5bb9\u91cd\u5b9a\u5411\u3002\u4e3a\u4e86\u8fdb\u884c\u8bf4\u660e\uff0c\u8bf7\u521b\u5efa\u4e00\u4e2a\u540d\u4e3a PointRef \u7684\u65b0\u7c7b\u7c7b\u578b\uff0c\u8be5\u7c7b\u7c7b\u578b\u4e0e Point \u7ed3\u6784\u5177\u6709\u76f8\u540c\u7684\u6210\u5458\uff0c\u800c\u4e0d\u662f\u91cd\u547d\u540d\u6784\u9020\u51fd\u6570\u4ee5\u5339\u914d\u7c7b\u540d\u3002<\/p>\n<p>\/\/ Classes are always reference types.<br \/>\n\/\/ \u7c7b\u59cb\u7ec8\u662f\u5f15\u7528\u7c7b\u578b\u3002<\/p>\n<p>class PointRef<br \/>\n{<br \/>\n\/\/ Same members as the Point structure...<br \/>\n\/\/ \u4e0ePoint\u7ed3\u6784\u76f8\u540c\u7684\u6210\u5458..<br \/>\n\/\/ Be sure to change your constructor name to PointRef!<br \/>\n\/\/ \u8bf7\u52a1\u5fc5\u5c06\u6784\u9020\u51fd\u6570\u540d\u79f0\u66f4\u6539\u4e3a PointRef\uff01<\/p>\n<p>public PointRef(int xPos, int yPos)<br \/>\n{<br \/>\nX = xPos;<br \/>\nY = yPos;<br \/>\n}<br \/>\n}<\/p>\n<p>Now, use your PointRef type within the following new method. Note that beyond using the PointRef class, rather than the Point structure, the code is identical to the ValueTypeAssignment() method.<br \/>\n\u73b0\u5728\uff0c\u5728\u4ee5\u4e0b\u65b0\u65b9\u6cd5\u4e2d\u4f7f\u7528\u60a8\u7684 PointRef \u7c7b\u578b\u3002\u8bf7\u6ce8\u610f\uff0c\u9664\u4e86\u4f7f\u7528 PointRef \u4e4b\u5916 \u7c7b\uff0c\u800c\u4e0d\u662f Point \u7ed3\u6784\uff0c\u4ee3\u7801\u4e0e ValueTypeAssignment\uff08\uff09 \u65b9\u6cd5\u76f8\u540c\u3002<\/p>\n<p>static void ReferenceTypeAssignment()<br \/>\n{<br \/>\nConsole.WriteLine(&quot;Assigning reference types\\n&quot;); PointRef p1 = new PointRef(10, 10);<br \/>\nPointRef p2 = p1;<\/p>\n<p>\/\/ Print both point refs.<br \/>\n\/\/ \u6253\u5370\u4e24\u4e2a\u70b9\u5f15\u7528\u3002<\/p>\n<p>p1.Display();<br \/>\np2.Display();<\/p>\n<p>\/\/ Change p1.X and print again.<br \/>\n\/\/ \u66f4\u6539 p1\u3002X \u5e76\u518d\u6b21\u6253\u5370\u3002<br \/>\np1.X = 100;<br \/>\nConsole.WriteLine(&quot;\\n=&gt; Changed p1.X\\n&quot;); p1.Display();<br \/>\np2.Display();<br \/>\n}<\/p>\n<p>In this case, you have two references pointing to the same object on the managed heap. Therefore, when you change the value of X using the p1 reference, p2.X reports the same value. Assuming you have called this new method, your output should look like the following:<br \/>\n\u5728\u8fd9\u79cd\u60c5\u51b5\u4e0b\uff0c\u60a8\u6709\u4e24\u4e2a\u5f15\u7528\u6307\u5411\u6258\u7ba1\u5806\u4e0a\u7684\u540c\u4e00\u5bf9\u8c61\u3002\u56e0\u6b64\uff0c\u5f53\u60a8\u4f7f\u7528 p1 \u5f15\u7528\u66f4\u6539 X \u7684\u503c\u65f6\uff0cp2\u3002X \u62a5\u544a\u76f8\u540c\u7684\u503c\u3002\u5047\u8bbe\u60a8\u5df2\u7ecf\u8c03\u7528\u4e86\u8fd9\u4e2a\u65b0\u65b9\u6cd5\uff0c\u60a8\u7684\u8f93\u51fa\u5e94\u5982\u4e0b\u6240\u793a\uff1a<\/p>\n<p>Assigning reference types X = 10, Y = 10<br \/>\nX = 10, Y = 10<br \/>\n=&gt; Changed p1.X X = 100, Y = 10<br \/>\nX = 100, Y = 10<\/p>\n<h2>Using Value Types Containing Reference Types<\/h2>\n<p>\u4f7f\u7528\u5305\u542b\u5f15\u7528\u7c7b\u578b\u7684\u503c\u7c7b\u578b<\/p>\n<p>Now that you have a better feeling for the basic differences between value types and reference types, let\u2019s examine a more complex example. Assume you have the following reference (class) type that maintains an informational string that can be set using a custom constructor:<br \/>\n\u73b0\u5728\uff0c\u60a8\u5bf9\u503c\u7c7b\u578b\u548c\u5f15\u7528\u7c7b\u578b\u4e4b\u95f4\u7684\u57fa\u672c\u5dee\u5f02\u6709\u4e86\u66f4\u597d\u7684\u4e86\u89e3\uff0c\u8ba9\u6211\u4eec\u770b\u4e00\u4e2a\u66f4\u590d\u6742\u7684\u793a\u4f8b\u3002\u5047\u8bbe\u60a8\u5177\u6709\u4ee5\u4e0b\u5f15\u7528\uff08\u7c7b\uff09\u7c7b\u578b\uff0c\u8be5\u7c7b\u578b\u7ef4\u62a4\u53ef\u4ee5\u4f7f\u7528\u81ea\u5b9a\u4e49\u6784\u9020\u51fd\u6570\u8bbe\u7f6e\u7684\u4fe1\u606f\u5b57\u7b26\u4e32\uff1a<\/p>\n<p>class ShapeInfo<br \/>\n{<br \/>\npublic string InfoString; public ShapeInfo(string info)<br \/>\n{<\/p>\n<p>InfoString = info;<br \/>\n}<br \/>\n}<\/p>\n<p>Now assume that you want to contain a variable of this class type within a value type named Rectangle.<br \/>\n\u73b0\u5728\u5047\u5b9a\u60a8\u5e0c\u671b\u5728\u540d\u4e3a Rectangle \u7684\u503c\u7c7b\u578b\u4e2d\u5305\u542b\u6b64\u7c7b\u7c7b\u578b\u7684\u53d8\u91cf\u3002<br \/>\nTo allow the caller to set the value of the inner ShapeInfo member variable, you also provide a custom constructor. Here is the complete definition of the Rectangle type:<br \/>\n\u82e5\u8981\u5141\u8bb8\u8c03\u7528\u65b9\u8bbe\u7f6e\u5185\u90e8 ShapeInfo \u6210\u5458\u53d8\u91cf\u7684\u503c\uff0c\u8fd8\u9700\u8981\u63d0\u4f9b\u81ea\u5b9a\u4e49\u6784\u9020\u51fd\u6570\u3002\u4e0b\u9762\u662f\u77e9\u5f62\u7c7b\u578b\u7684\u5b8c\u6574\u5b9a\u4e49\uff1a<\/p>\n<p>struct Rectangle<br \/>\n{<br \/>\n\/\/ The Rectangle structure contains a reference type member. public ShapeInfo RectInfo;<\/p>\n<p>public int RectTop, RectLeft, RectBottom, RectRight;<\/p>\n<p>public Rectangle(string info, int top, int left, int bottom, int right)<br \/>\n{<br \/>\nRectInfo = new ShapeInfo(info); RectTop = top; RectBottom = bottom; RectLeft = left; RectRight = right;<br \/>\n}<\/p>\n<p>public void Display()<br \/>\n{<br \/>\nConsole.WriteLine(&quot;String = {0}, Top = {1}, Bottom = {2}, &quot; + &quot;Left = {3}, Right = {4}&quot;,<br \/>\nRectInfo.InfoString, RectTop, RectBottom, RectLeft, RectRight);<br \/>\n}<br \/>\n}<\/p>\n<p>At this point, you have contained a reference type within a value type. The million-dollar question now becomes \u201cWhat happens if you assign one Rectangle variable to another?\u201d Given what you already know about value types, you would be correct in assuming that the integer data (which is indeed a structure, System.Int32) should be an independent entity for each Rectangle variable. But what about the internal reference type? Will the object\u2019s state be fully copied, or will the reference to that object be copied? To answer this question, define the following method and invoke it:<br \/>\n\u6b64\u65f6\uff0c\u60a8\u5df2\u5728\u503c\u7c7b\u578b\u4e2d\u5305\u542b\u5f15\u7528\u7c7b\u578b\u3002\u767e\u4e07\u7f8e\u5143\u7684\u95ee\u9898\u73b0\u5728\u53d8\u6210\u4e86\u201c\u5982\u679c\u5c06\u4e00\u4e2a\u77e9\u5f62\u53d8\u91cf\u5206\u914d\u7ed9\u53e6\u4e00\u4e2a\u77e9\u5f62\u53d8\u91cf\u4f1a\u53d1\u751f\u4ec0\u4e48\uff1f\u9274\u4e8e\u60a8\u5df2\u7ecf\u4e86\u89e3\u7684\u503c\u7c7b\u578b\uff0c\u5047\u8bbe\u6574\u6570\u6570\u636e\uff08\u5b9e\u9645\u4e0a\u662f\u4e00\u4e2a\u7ed3\u6784\uff0cSystem.Int32\uff09\u5e94\u8be5\u662f\u6bcf\u4e2a\u77e9\u5f62\u53d8\u91cf\u7684\u72ec\u7acb\u5b9e\u4f53\uff0c\u8fd9\u662f\u6b63\u786e\u7684\u3002\u4f46\u662f\u5185\u90e8\u53c2\u8003\u7c7b\u578b\u5462\uff1f\u662f\u5b8c\u5168\u590d\u5236\u5bf9\u8c61\u7684\u72b6\u6001\uff0c\u8fd8\u662f\u590d\u5236\u5bf9\u8be5\u5bf9\u8c61\u7684\u5f15\u7528\uff1f\u82e5\u8981\u56de\u7b54\u6b64\u95ee\u9898\uff0c\u8bf7\u5b9a\u4e49\u4ee5\u4e0b\u65b9\u6cd5\u5e76\u8c03\u7528\u5b83\uff1a<\/p>\n<p>static void ValueTypeContainingRefType()<br \/>\n{<br \/>\n\/\/ Create the first Rectangle.<br \/>\n\/\/ \u521b\u5efa\u7b2c\u4e00\u4e2a\u77e9\u5f62\u3002<br \/>\nConsole.WriteLine(&quot;-&gt; Creating r1&quot;);<br \/>\nRectangle r1 = new Rectangle(&quot;First Rect&quot;, 10, 10, 50, 50);<\/p>\n<p>\/\/ Now assign a new Rectangle to r1.<br \/>\n\/\/ \u73b0\u5728\u4e3a r1 \u5206\u914d\u4e00\u4e2a\u65b0\u7684\u77e9\u5f62\u3002<br \/>\nConsole.WriteLine(&quot;-&gt; Assigning r2 to r1&quot;); Rectangle r2 = r1;<\/p>\n<p>\/\/ Change some values of r2.<br \/>\n\/\/ \u66f4\u6539 r2 \u7684\u67d0\u4e9b\u503c\u3002<br \/>\nConsole.WriteLine(&quot;-&gt; Changing values of r2&quot;); r2.RectInfo.InfoString = &quot;This is new info!&quot;; r2.RectBottom = 4444;<\/p>\n<p>\/\/ Print values of both rectangles. r1.Display();<br \/>\nr2.Display();<br \/>\n}<\/p>\n<p>The output is shown here:<br \/>\n\u8f93\u51fa\u5982\u4e0b\u6240\u793a\uff1a<\/p>\n<p>-&gt; Creating r1<\/p>\n<p>-&gt; Assigning r2 to r1<br \/>\n-&gt; Changing values of r2<br \/>\nString = This is new info!, Top = 10, Bottom = 50, Left = 10, Right = 50 String = This is new info!, Top = 10, Bottom = 4444, Left = 10, Right = 50<\/p>\n<p>As you can see, when you change the value of the informational string using the r2 reference, the r1 reference displays the same value. By default, when a value type contains other reference types, assignment results in a copy of the references. In this way, you have two independent structures, each of which contains a reference pointing to the same object in memory (i.e., a shallow copy). When you want to perform a deep copy, where the state of internal references is fully copied into a new object, one approach is to implement the ICloneable interface (as you will do in Chapter 8).<br \/>\n\u5982\u60a8\u6240\u89c1\uff0c\u5f53\u60a8\u4f7f\u7528 r2 \u5f15\u7528\u66f4\u6539\u4fe1\u606f\u5b57\u7b26\u4e32\u7684\u503c\u65f6\uff0cr1 \u5f15\u7528\u5c06\u663e\u793a\u76f8\u540c\u7684\u503c\u3002\u9ed8\u8ba4\u60c5\u51b5\u4e0b\uff0c\u5f53\u503c\u7c7b\u578b\u5305\u542b\u5176\u4ed6\u5f15\u7528\u7c7b\u578b\u65f6\uff0c\u8d4b\u503c\u5c06\u751f\u6210\u5f15\u7528\u7684\u526f\u672c\u3002\u8fd9\u6837\uff0c\u60a8\u5c31\u6709\u4e24\u4e2a\u72ec\u7acb\u7684\u7ed3\u6784\uff0c\u6bcf\u4e2a\u7ed3\u6784\u90fd\u5305\u542b\u4e00\u4e2a\u6307\u5411\u5185\u5b58\u4e2d\u540c\u4e00\u5bf9\u8c61\u7684\u5f15\u7528\uff08\u5373\u6d45\u62f7\u8d1d\uff09\u3002\u5f53\u60a8\u60f3\u8981\u6267\u884c\u6df1\u5c42\u590d\u5236\u65f6\uff0c\u5c06\u5185\u90e8\u5f15\u7528\u7684\u72b6\u6001\u5b8c\u5168\u590d\u5236\u5230\u65b0\u5bf9\u8c61\u4e2d\uff0c\u4e00\u79cd\u65b9\u6cd5\u662f\u5b9e\u73b0 ICloneable \u63a5\u53e3\uff08\u5982\u7b2c 8 \u7ae0\u4e2d\u6240\u8ff0\uff09\u3002<\/p>\n<h2>Passing Reference Types by Value<\/h2>\n<p>\u6309\u503c\u4f20\u9012\u5f15\u7528\u7c7b\u578b<\/p>\n<p>As covered earlier in the chapter, reference types or value types can be passed as parameters to methods. However, passing a reference type (e.g., a class) by reference is quite different from passing it by value. To understand the distinction, assume you have a simple Person class defined in a new Console Application project named FunWithRefTypeValTypeParams, defined as follows:<br \/>\n\u5982\u672c\u7ae0\u524d\u9762\u6240\u8ff0\uff0c\u5f15\u7528\u7c7b\u578b\u6216\u503c\u7c7b\u578b\u53ef\u4ee5\u4f5c\u4e3a\u53c2\u6570\u4f20\u9012\u7ed9\u65b9\u6cd5\u3002\u4f46\u662f\uff0c\u901a\u8fc7\u5f15\u7528\u4f20\u9012\u5f15\u7528\u7c7b\u578b\uff08\u4f8b\u5982\uff0c\u7c7b\uff09\u4e0e\u6309\u503c\u4f20\u9012\u5f15\u7528\u7c7b\u578b\u5b8c\u5168\u4e0d\u540c\u3002\u82e5\u8981\u7406\u89e3\u8fd9\u79cd\u533a\u522b\uff0c\u5047\u8bbe\u60a8\u5728\u540d\u4e3a FunWithRefTypeValTypeParams \u7684\u65b0\u63a7\u5236\u53f0\u5e94\u7528\u7a0b\u5e8f\u9879\u76ee\u4e2d\u5b9a\u4e49\u4e86\u4e00\u4e2a\u7b80\u5355\u7684 Person \u7c7b\uff0c\u5b9a\u4e49\u5982\u4e0b\uff1a<\/p>\n<p>class Person<br \/>\n{<br \/>\npublic string personName; public int personAge;<\/p>\n<p>\/\/ Constructors.<br \/>\npublic Person(string name, int age)<br \/>\n{<br \/>\npersonName = name; personAge = age;<br \/>\n}<br \/>\npublic Person(){}<\/p>\n<p>public void Display()<br \/>\n{<br \/>\nConsole.WriteLine(&quot;Name: {0}, Age: {1}&quot;, personName, personAge);<br \/>\n}<br \/>\n}<\/p>\n<p>Now, what if you create a method that allows the caller to send in the Person object by value (note the lack of parameter modifiers, such as out or ref)?<br \/>\n\u73b0\u5728\uff0c\u5982\u679c\u60a8\u521b\u5efa\u4e00\u4e2a\u5141\u8bb8\u8c03\u7528\u65b9\u6309\u503c\u53d1\u9001 Person \u5bf9\u8c61\u7684\u65b9\u6cd5\uff08\u8bf7\u6ce8\u610f\u7f3a\u5c11\u53c2\u6570\u4fee\u9970\u7b26\uff0c\u4f8b\u5982 out \u6216 ref\uff09\u600e\u4e48\u529e\uff1f<\/p>\n<p>static void SendAPersonByValue(Person p)<br \/>\n{<br \/>\n\/\/ Change the age of &quot;p&quot;?<br \/>\n\/\/ \u66f4\u6539\u201cp\u201d\u7684\u5e74\u9f84\uff1f<br \/>\np.personAge = 99;<\/p>\n<p>\/\/ Will the caller see this reassignment?<br \/>\n\/\/ \u547c\u53eb\u8005\u4f1a\u770b\u5230\u6b64\u91cd\u65b0\u5206\u914d\u5417\uff1f<br \/>\np = new Person(&quot;Nikki&quot;, 99);<br \/>\n}<\/p>\n<p>Notice how the SendAPersonByValue() method attempts to reassign the incoming Person reference to a new Person object, as well as change some state data. Now let\u2019s test this method using the following code:<br \/>\n\u8bf7\u6ce8\u610f SendAPersonByValue\uff08\uff09 \u65b9\u6cd5\u5982\u4f55\u5c1d\u8bd5\u5c06\u4f20\u5165\u7684 Person \u5f15\u7528\u91cd\u65b0\u5206\u914d\u7ed9\u65b0\u7684 Person \u5bf9\u8c61\uff0c\u4ee5\u53ca\u66f4\u6539\u4e00\u4e9b\u72b6\u6001\u6570\u636e\u3002\u73b0\u5728\uff0c\u8ba9\u6211\u4eec\u4f7f\u7528\u4ee5\u4e0b\u4ee3\u7801\u6d4b\u8bd5\u6b64\u65b9\u6cd5\uff1a<\/p>\n<p>\/\/ Passing ref-types by value.<br \/>\n\/\/ \u6309\u503c\u4f20\u9012\u5f15\u7528\u7c7b\u578b\u3002<br \/>\nConsole.WriteLine(&quot;<strong><strong><em> Passing Person object by value <\/em><\/strong><\/strong>&quot;);<br \/>\nPerson fred = new Person(&quot;Fred&quot;, 12);<br \/>\nConsole.WriteLine(&quot;\\nBefore by value call, Person is:&quot;);<br \/>\nfred.Display();<\/p>\n<p>SendAPersonByValue(fred);<br \/>\nConsole.WriteLine(&quot;\\nAfter by value call, Person is:&quot;); fred.Display();<br \/>\nConsole.ReadLine();<\/p>\n<p>The following is the output of this call:<br \/>\n\u4ee5\u4e0b\u662f\u6b64\u8c03\u7528\u7684\u8f93\u51fa\uff1a<\/p>\n<p><strong><strong><em> Passing Person object by value <\/em><\/strong><\/strong><br \/>\nBefore by value call, Person is:<br \/>\nName: Fred, Age: 12<br \/>\nAfter by value call, Person is:<br \/>\nName: Fred, Age: 99<\/p>\n<p>As you can see, the value of personAge has been modified. This behavior, discussed earlier, should make more sense now that you understand the way reference types work. Given that you were able to change the state of the incoming Person, what was copied? The answer: a copy of the reference to the caller\u2019s object.<br \/>\n\u5982\u60a8\u6240\u89c1\uff0cpersonAge \u7684\u503c\u5df2\u88ab\u4fee\u6539\u3002\u524d\u9762\u8ba8\u8bba\u7684\u6b64\u884c\u4e3a\u5e94\u8be5\u66f4\u6709\u610f\u4e49\uff0c\u56e0\u4e3a\u60a8\u4e86\u89e3\u4e86\u5f15\u7528\u7c7b\u578b\u7684\u5de5\u4f5c\u65b9\u5f0f\u3002\u65e2\u7136\u60a8\u80fd\u591f\u66f4\u6539\u4f20\u5165\u4eba\u5458\u7684\u72b6\u6001\uff0c\u5219\u590d\u5236\u4e86\u4ec0\u4e48\uff1f\u7b54\u6848\uff1a\u5bf9\u8c03\u7528\u65b9\u5bf9\u8c61\u7684\u5f15\u7528\u7684\u526f\u672c\u3002<br \/>\nTherefore, as the SendAPersonByValue() method is pointing to the same object as the caller, it is possible to alter the object\u2019s state data. What is not possible is to reassign what the reference is pointing to.<br \/>\n\u56e0\u6b64\uff0c\u7531\u4e8e SendAPersonByValue\uff08\uff09 \u65b9\u6cd5\u6307\u5411\u4e0e\u8c03\u7528\u65b9\u76f8\u540c\u7684\u5bf9\u8c61\uff0c\u56e0\u6b64\u53ef\u4ee5\u66f4\u6539\u5bf9\u8c61\u7684\u72b6\u6001\u6570\u636e\u3002\u4e0d\u53ef\u80fd\u7684\u662f\u91cd\u65b0\u5206\u914d\u5f15\u7528\u6240\u6307\u5411\u7684\u5185\u5bb9\u3002<\/p>\n<h2>Passing Reference Types by Reference<\/h2>\n<p>\u6309\u5f15\u7528\u4f20\u9012\u5f15\u7528\u7c7b\u578b<br \/>\nNow assume you have a SendAPersonByReference() method, which passes a reference type by reference (note the ref parameter modifier).<br \/>\n\u73b0\u5728\u5047\u8bbe\u4f60\u6709\u4e00\u4e2a SendAPersonByReference\uff08\uff09 \u65b9\u6cd5\uff0c\u5b83\u901a\u8fc7\u5f15\u7528\u4f20\u9012\u5f15\u7528\u7c7b\u578b\uff08\u8bf7\u6ce8\u610f ref \u53c2\u6570\u4fee\u9970\u7b26\uff09\u3002<\/p>\n<p>static void SendAPersonByReference(ref Person p)<br \/>\n{<br \/>\n\/\/ Change some data of &quot;p&quot;. p.personAge = 555;<\/p>\n<p>\/\/ &quot;p&quot; is now pointing to a new object on the heap! p = new Person(&quot;Nikki&quot;, 999);<br \/>\n}<\/p>\n<p>As you might expect, this allows complete flexibility of how the callee is able to manipulate the incoming parameter. Not only can the callee change the state of the object, but if it so chooses, it may also reassign the reference to a new Person object. Now ponder the following updated code:<br \/>\n\u5982\u60a8\u6240\u6599\uff0c\u8fd9\u5141\u8bb8\u88ab\u8c03\u7528\u65b9\u5982\u4f55\u64cd\u4f5c\u4f20\u5165\u53c2\u6570\u7684\u5b8c\u5168\u7075\u6d3b\u6027\u3002\u88ab\u8c03\u7528\u65b9\u4e0d\u4ec5\u53ef\u4ee5\u66f4\u6539\u5bf9\u8c61\u7684\u72b6\u6001\uff0c\u800c\u4e14\u5982\u679c\u5b83\u9009\u62e9\u8fd9\u6837\u505a\uff0c\u5b83\u8fd8\u53ef\u4ee5\u5c06\u5f15\u7528\u91cd\u65b0\u5206\u914d\u7ed9\u65b0\u7684 Person \u5bf9\u8c61\u3002\u73b0\u5728\u8003\u8651\u4ee5\u4e0b\u66f4\u65b0\u7684\u4ee3\u7801\uff1a<\/p>\n<p>\/\/ Passing ref-types by ref.<br \/>\n\/\/ \u901a\u8fc7\u5f15\u7528\u4f20\u9012\u5f15\u7528\u7c7b\u578b\u3002<br \/>\nConsole.WriteLine(&quot;<strong><strong><em> Passing Person object by reference <\/em><\/strong><\/strong>&quot;);<br \/>\n...<\/p>\n<p>Person mel = new Person(&quot;Mel&quot;, 23); Console.WriteLine(&quot;Before by ref call, Person is:&quot;); mel.Display();<\/p>\n<p>SendAPersonByReference(ref mel); Console.WriteLine(&quot;After by ref call, Person is:&quot;); mel.Display();<br \/>\nConsole.ReadLine();<\/p>\n<p>Notice the following output:<br \/>\n\u8bf7\u6ce8\u610f\u4ee5\u4e0b\u8f93\u51fa\uff1a<br \/>\n<strong><strong><em> Passing Person object by reference <\/em><\/strong><\/strong> Before by ref call, Person is:<br \/>\nName: Mel, Age: 23<br \/>\nAfter by ref call, Person is:<br \/>\nName: Nikki, Age: 999<\/p>\n<p>As you can see, an object named Mel returns after the call as an object named Nikki, as the method was able to change what the incoming reference pointed to in memory. The golden rule to keep in mind when passing reference types is the following:<br \/>\n\u5982\u60a8\u6240\u89c1\uff0c\u540d\u4e3a Mel \u7684\u5bf9\u8c61\u5728\u8c03\u7528\u540e\u4f5c\u4e3a\u540d\u4e3a Nikki \u7684\u5bf9\u8c61\u8fd4\u56de\uff0c\u56e0\u4e3a\u8be5\u65b9\u6cd5\u80fd\u591f\u66f4\u6539\u4f20\u5165\u5f15\u7528\u5728\u5185\u5b58\u4e2d\u6307\u5411\u7684\u5185\u5bb9\u3002\u4f20\u9012\u5f15\u7528\u7c7b\u578b\u65f6\u8981\u8bb0\u4f4f\u7684\u9ec4\u91d1\u6cd5\u5219\u5982\u4e0b\uff1a<br \/>\n\u2022   If a reference type is passed by reference, the callee may change the values of the object\u2019s state data, as well as the object it is referencing.<br \/>\n\u5982\u679c\u5f15\u7528\u7c7b\u578b\u662f\u901a\u8fc7\u5f15\u7528\u4f20\u9012\u7684\uff0c\u5219\u88ab\u8c03\u7528\u65b9\u53ef\u4ee5\u66f4\u6539\u5bf9\u8c61\u7684\u72b6\u6001\u6570\u636e\u7684\u503c\u4ee5\u53ca\u5b83\u6240\u5f15\u7528\u7684\u5bf9\u8c61\u3002<br \/>\n\u2022   If a reference type is passed by value, the callee may change the values of the object\u2019s state data but not the object it is referencing.<br \/>\n\u5982\u679c\u5f15\u7528\u7c7b\u578b\u662f\u6309\u503c\u4f20\u9012\u7684\uff0c\u5219\u88ab\u8c03\u7528\u65b9\u53ef\u4ee5\u66f4\u6539\u5bf9\u8c61\u7684\u72b6\u6001\u6570\u636e\u7684\u503c\uff0c\u4f46\u4e0d\u80fd\u66f4\u6539\u5b83\u6240\u5f15\u7528\u7684\u5bf9\u8c61\u7684\u503c\u3002<\/p>\n<h2>Final Details Regarding Value Types and Reference Types<\/h2>\n<p>\u6709\u5173\u503c\u7c7b\u578b\u548c\u5f15\u7528\u7c7b\u578b\u7684\u6700\u7ec8\u8be6\u7ec6\u4fe1\u606f<\/p>\n<p>To wrap up this topic, consider the information in Table 4-4, which summarizes the core distinctions between value types and reference types.<br \/>\n\u4e3a\u4e86\u603b\u7ed3\u672c\u4e3b\u9898\uff0c\u8bf7\u8003\u8651\u8868 4-4 \u4e2d\u7684\u4fe1\u606f\uff0c\u5176\u4e2d\u603b\u7ed3\u4e86\u503c\u7c7b\u578b\u548c\u5f15\u7528\u7c7b\u578b\u4e4b\u95f4\u7684\u6838\u5fc3\u533a\u522b\u3002<\/p>\n<p>Table 4-4. Value Types and Reference Types Comparison<br \/>\n\u6709\u5173\u503c\u7c7b\u578b\u548c\u5f15\u7528\u7c7b\u578b\u7684\u6700\u7ec8\u8be6\u7ec6\u4fe1\u606f<\/p>\n<table>\n<tr>\n<td>Intriguing Question<br \/>\n        \u8010\u4eba\u5bfb\u5473\u7684\u95ee\u9898<\/td>\n<td>Value Type<br \/>\n        \u503c\u7c7b\u578b<\/td>\n<td>Reference Type<br \/>\n        \u5f15\u7528\u7c7b\u578b<\/td>\n<\/tr>\n<tr>\n<td>Where are objects allocated?<br \/>\n        \u5bf9\u8c61\u5206\u914d\u5728\u54ea\u91cc\uff1f<\/td>\n<td>Allocated on the stack.<br \/>\n        \u5728\u5806\u6808\u4e0a\u5206\u914d\u3002<\/td>\n<td>Allocated on the managed heap.<br \/>\n        \u5728\u6258\u7ba1\u5806\u4e0a\u5206\u914d\u3002<\/td>\n<\/tr>\n<tr>\n<td>How is a variable represented?<br \/>\n        \u53d8\u91cf\u5982\u4f55\u8868\u793a\uff1f<\/td>\n<td>Value type variables are local copies.<br \/>\n        \u503c\u7c7b\u578b\u53d8\u91cf\u662f\u672c\u5730\u526f\u672c\u3002<\/td>\n<td>Reference type variables are pointing to the memory occupied by the allocated instance.<br \/>\n        \u5f15\u7528\u7c7b\u578b\u53d8\u91cf\u6307\u5411\u5206\u914d\u7684\u5b9e\u4f8b\u5360\u7528\u7684\u5185\u5b58\u3002<\/td>\n<\/tr>\n<tr>\n<td>What is the base type?<br \/>\n        \u57fa\u672c\u7c7b\u578b\u662f\u4ec0\u4e48\uff1f<\/td>\n<td>Implicitly extends\u00a0System. ValueType.<br \/>\n        \u9690\u5f0f\u6269\u5c55\u7cfb\u7edf\u3002\u503c\u7c7b\u578b\u3002<\/td>\n<td>Can derive from any other type (except\u00a0System.ValueType), if that type is not \u201csealed\u201d (more details on this in Chapter\u00a06).<br \/>\n        \u53ef\u4ee5\u4ece\u4efb\u4f55\u5176\u4ed6\u7c7b\u578b\uff08System.ValueType \u9664\u5916\uff09\u6d3e\u751f\uff0c\u5982\u679c\u8be5\u7c7b\u578b\u4e0d\u662f\u201c\u5bc6\u5c01\u7684\u201d\uff08\u6709\u5173\u6b64\u5185\u5bb9\u7684\u66f4\u591a\u8be6\u7ec6\u4fe1\u606f\uff0c\u8bf7\u53c2\u89c1\u7b2c 6 \u7ae0\uff09\u3002<\/td>\n<\/tr>\n<tr>\n<td>Can this type function as a base to other types?<br \/>\n        \u6b64\u7c7b\u578b\u53ef\u4ee5\u4f5c\u4e3a\u5176\u4ed6\u7c7b\u578b\u7684\u57fa\u7840\u5417\uff1f<\/td>\n<td>No. Value types are always sealed and cannot be inherited from.<br \/>\n        \u4e0d\u3002\u503c\u7c7b\u578b\u59cb\u7ec8\u662f\u5bc6\u5c01\u7684\uff0c\u4e0d\u80fd\u4ece\u4e2d\u7ee7\u627f\u3002<\/td>\n<td>Yes. If the type is not sealed, it may function as a base to other types.<br \/>\n        \u662f\u7684\u3002\u5982\u679c\u8be5\u7c7b\u578b\u672a\u5bc6\u5c01\uff0c\u5219\u53ef\u4ee5\u7528\u4f5c\u5176\u4ed6\u7c7b\u578b\u7684\u57fa\u7840\u3002<\/td>\n<\/tr>\n<tr>\n<td>What is the default parameter-passing behavior?<br \/>\n        \u9ed8\u8ba4\u53c2\u6570\u4f20\u9012\u884c\u4e3a\u662f\u4ec0\u4e48\uff1f<\/td>\n<td>Variables are passed by value (i.e., a copy of the variable is passed into the called function).<br \/>\n        \u9ed8\u8ba4\u53c2\u6570\u4f20\u9012\u884c\u4e3a\u662f\u4ec0\u4e48\uff1f<\/td>\n<td>For reference types, the reference is copied by value.<br \/>\n        \u5bf9\u4e8e\u5f15\u7528\u7c7b\u578b\uff0c\u5f15\u7528\u6309\u503c\u590d\u5236\u3002<\/td>\n<\/tr>\n<tr>\n<td>Can this type override\u00a0System. Object.Finalize()?<br \/>\n        \u6b64\u7c7b\u578b\u53ef\u4ee5\u8986\u76d6System. Object.Finalize()\uff1f<\/td>\n<td>No.<br \/>\n        \u4e0d\u3002<\/td>\n<td>Yes, indirectly (more details on this in Chapter\u00a09).<br \/>\n        \u662f\u7684\uff0c\u95f4\u63a5\u7684\uff08\u6709\u5173\u6b64\u5185\u5bb9\u7684\u66f4\u591a\u8be6\u7ec6\u4fe1\u606f\u8bf7\u53c2\u9605\u7b2c9\u7ae0\uff09\u3002<\/td>\n<\/tr>\n<tr>\n<td>Can I define constructors for this type?<br \/>\n        \u6211\u53ef\u4ee5\u4e3a\u6b64\u7c7b\u578b\u5b9a\u4e49\u6784\u9020\u51fd\u6570\u5417\uff1f<\/td>\n<td>Yes, but the default constructor is reserved (i.e., your custom constructors must all have arguments).<br \/>\n        \u662f\u7684\uff0c\u4f46\u9ed8\u8ba4\u6784\u9020\u51fd\u6570\u662f\u4fdd\u7559\u7684\uff08\u5373\uff0c\u60a8\u7684\u81ea\u5b9a\u4e49\u6784\u9020\u51fd\u6570\u5fc5\u987b\u5168\u90e8\u5177\u6709\u53c2\u6570\uff09\u3002<\/td>\n<td>But of course!<br \/>\n        \u4f46\u662f\u5f53\u7136\uff01<\/td>\n<\/tr>\n<tr>\n<td>When do variables of this type die?<br \/>\n        \u8fd9\u79cd\u7c7b\u578b\u7684\u53d8\u91cf\u4ec0\u4e48\u65f6\u5019\u6b7b\u4ea1\uff1f<\/td>\n<td>When they fall out of the defining scope.<br \/>\n        \u5f53\u5b83\u4eec\u8d85\u51fa\u5b9a\u4e49\u8303\u56f4\u65f6\u3002<\/td>\n<td>When the object is garbage collected (see Chapter\u00a09).<br \/>\n        \u5f53\u5bf9\u8c61\u88ab\u5783\u573e\u56de\u6536\u65f6\uff08\u8bf7\u53c2\u9605\u7b2c 9 \u7ae0\uff09\u3002<\/td>\n<\/tr>\n<\/table>\n<p>Despite their differences, value types and reference types both can implement interfaces and may support any number of fields, methods, overloaded operators, constants, properties, and events.<br \/>\n\u5c3d\u7ba1\u5b58\u5728\u5dee\u5f02\uff0c\u4f46\u503c\u7c7b\u578b\u548c\u5f15\u7528\u7c7b\u578b\u90fd\u53ef\u4ee5\u5b9e\u73b0\u63a5\u53e3\uff0c\u5e76\u4e14\u53ef\u4ee5\u652f\u6301\u4efb\u610f\u6570\u91cf\u7684\u5b57\u6bb5\u3001\u65b9\u6cd5\u3001\u91cd\u8f7d\u8fd0\u7b97\u7b26\u3001\u5e38\u91cf\u3001\u5c5e\u6027\u548c\u4e8b\u4ef6\u3002<\/p>\n<h2>Understanding C# Nullable Types<\/h2>\n<p>\u4e86\u89e3 C# \u53ef\u4e3a\u7a7a\u7684\u7c7b\u578b<br \/>\nLet\u2019s examine the role of the nullable data type using a Console Application project named FunWithNullableValueTypes. As you know, C# data types have a fixed range and are represented as a type in the System namespace. For example, the System.Boolean data type can be assigned a value from the set<br \/>\n{true, false}. Now, recall that all the numerical data types (as well as the Boolean data type) are value types. Value types can never be assigned the value of null, as that is used to establish an empty object reference.<br \/>\n\u8ba9\u6211\u4eec\u4f7f\u7528\u540d\u4e3a FunWithNullableValueType \u7684\u63a7\u5236\u53f0\u5e94\u7528\u7a0b\u5e8f\u9879\u76ee\u68c0\u67e5\u53ef\u4e3a\u7a7a\u6570\u636e\u7c7b\u578b\u7684\u89d2\u8272\u3002\u5982\u60a8\u6240\u77e5\uff0cC# \u6570\u636e\u7c7b\u578b\u5177\u6709\u56fa\u5b9a\u8303\u56f4\uff0c\u5e76\u5728 System \u547d\u540d\u7a7a\u95f4\u4e2d\u8868\u793a\u4e3a\u4e00\u79cd\u7c7b\u578b\u3002\u4f8b\u5982\uff0c\u53ef\u4ee5\u4ece\u96c6\u5408\u4e2d\u4e3a System.Boolean \u6570\u636e\u7c7b\u578b\u5206\u914d\u4e00\u4e2a\u503c{\u771f\uff0c\u5047}\u3002\u73b0\u5728\uff0c\u56de\u60f3\u4e00\u4e0b\uff0c\u6240\u6709\u6570\u503c\u6570\u636e\u7c7b\u578b\uff08\u4ee5\u53ca\u5e03\u5c14\u6570\u636e\u7c7b\u578b\uff09\u90fd\u662f\u503c\u7c7b\u578b\u3002\u6c38\u8fdc\u4e0d\u80fd\u4e3a\u503c\u7c7b\u578b\u5206\u914d null \u503c\uff0c\u56e0\u4e3a\u5b83\u7528\u4e8e\u5efa\u7acb\u7a7a\u5bf9\u8c61\u53c2\u8003\u3002<\/p>\n<p>\/\/ Compiler errors!<br \/>\n\/\/ Value types cannot be set to null! bool myBool = null;<br \/>\nint myInt = null;<\/p>\n<p>C# supports the concept of nullable data types. Simply put, a nullable type can represent all the values of its underlying type, plus the value null. Thus, if you declare a nullable bool, it could be assigned a value from the set {true, false, null}. This can be extremely helpful when working with relational databases, given that it is quite common to encounter undefined columns in database tables. Without the concept of a nullable data type, there is no convenient manner in C# to represent a numerical data point with no value.<br \/>\nC# \u652f\u6301\u53ef\u4e3a null \u7684\u6570\u636e\u7c7b\u578b\u7684\u6982\u5ff5\u3002\u7b80\u5355\u5730\u8bf4\uff0c\u53ef\u4e3a null \u7684\u7c7b\u578b\u53ef\u4ee5\u8868\u793a\u5176\u57fa\u7840\u7c7b\u578b\u7684\u6240\u6709\u503c\u4ee5\u53ca\u503c null\u3002\u56e0\u6b64\uff0c\u5982\u679c\u4f60\u58f0\u660e\u4e00\u4e2a\u53ef\u4e3a\u7a7a\u7684\u5e03\u5c14\u503c\uff0c\u5219\u53ef\u4ee5\u4ece\u96c6\u5408 {true\uff0c false\uff0c null} \u4e2d\u4e3a\u5176\u8d4b\u503c\u3002\u8fd9\u5728\u4f7f\u7528\u5173\u7cfb\u6570\u636e\u5e93\u65f6\u975e\u5e38\u6709\u7528\uff0c\u56e0\u4e3a\u5728\u6570\u636e\u5e93\u8868\u4e2d\u9047\u5230\u672a\u5b9a\u4e49\u7684\u5217\u662f\u5f88\u5e38\u89c1\u7684\u3002\u5982\u679c\u6ca1\u6709\u53ef\u4e3a null \u7684\u6570\u636e\u7c7b\u578b\u7684\u6982\u5ff5\uff0cC# \u4e2d\u5c31\u6ca1\u6709\u65b9\u4fbf\u7684\u65b9\u5f0f\u6765\u8868\u793a\u6ca1\u6709\u503c\u7684\u6570\u503c\u6570\u636e\u70b9\u3002<\/p>\n<p>To define a nullable variable type, the question mark symbol (?) is suffixed to the underlying data type. Like a non-nullable variable, local nullable variables must be assigned an initial value before you can use them.<br \/>\nC# \u652f\u6301\u53ef\u4e3a null \u7684\u6570\u636e\u7c7b\u578b\u7684\u6982\u5ff5\u3002\u7b80\u5355\u5730\u8bf4\uff0c\u53ef\u4e3a null \u7684\u7c7b\u578b\u53ef\u4ee5\u8868\u793a\u5176\u57fa\u7840\u7c7b\u578b\u7684\u6240\u6709\u503c\u4ee5\u53ca\u503c null\u3002\u56e0\u6b64\uff0c\u5982\u679c\u4f60\u58f0\u660e\u4e00\u4e2a\u53ef\u4e3a\u7a7a\u7684\u5e03\u5c14\u503c\uff0c\u5219\u53ef\u4ee5\u4ece\u96c6\u5408 {true\uff0c false\uff0c null} \u4e2d\u4e3a\u5176\u8d4b\u503c\u3002\u8fd9\u5728\u4f7f\u7528\u5173\u7cfb\u6570\u636e\u5e93\u65f6\u975e\u5e38\u6709\u7528\uff0c\u56e0\u4e3a\u5728\u6570\u636e\u5e93\u8868\u4e2d\u9047\u5230\u672a\u5b9a\u4e49\u7684\u5217\u662f\u5f88\u5e38\u89c1\u7684\u3002\u5982\u679c\u6ca1\u6709\u53ef\u4e3a null \u7684\u6570\u636e\u7c7b\u578b\u7684\u6982\u5ff5\uff0cC# \u4e2d\u5c31\u6ca1\u6709\u65b9\u4fbf\u7684\u65b9\u5f0f\u6765\u8868\u793a\u6ca1\u6709\u503c\u7684\u6570\u503c\u6570\u636e\u70b9\u3002<\/p>\n<p>static void LocalNullableVariables()<br \/>\n{<br \/>\n\/\/ Define some local nullable variables. int? nullableInt = 10;<br \/>\ndouble? nullableDouble = 3.14; bool? nullableBool = null; char? nullableChar = 'a';<br \/>\nint?[] arrayOfNullableInts = new int?[10];<br \/>\n}<\/p>\n<h2>Using Nullable Value Types<\/h2>\n<p>\u4f7f\u7528\u53ef\u4e3a\u7a7a\u7684\u503c\u7c7b\u578b<\/p>\n<p>In C#, the ? suffix notation is a shorthand for creating an instance of the generic System.Nullable<T> structure type. It is also used for creating nullable reference types (covered in the next section), although the behavior is a bit different. While you will not examine generics until Chapter 10, it is important to understand that the System.Nullable<T> type provides a set of members that all nullable types can make use of.<br \/>\n\u5728 C# \u4e2d\uff0c\uff1f \u540e\u7f00\u8868\u793a\u6cd5\u662f\u521b\u5efa\u901a\u7528 System.Nullable<T> \u7ed3\u6784\u7c7b\u578b\u5b9e\u4f8b\u7684\u7b80\u5199\u3002\u5b83\u8fd8\u7528\u4e8e\u521b\u5efa\u53ef\u4e3a null \u7684\u5f15\u7528\u7c7b\u578b\uff08\u5c06\u5728\u4e0b\u4e00\u8282\u4e2d\u4ecb\u7ecd\uff09\uff0c\u5c3d\u7ba1\u884c\u4e3a\u7565\u6709\u4e0d\u540c\u3002\u867d\u7136\u5728\u7b2c 10 \u7ae0\u4e4b\u524d\u4e0d\u4f1a\u68c0\u67e5\u6cdb\u578b\uff0c\u4f46\u91cd\u8981\u7684\u662f\u8981\u4e86\u89e3 System.Nullable<T> \u7c7b\u578b\u63d0\u4f9b\u4e86\u4e00\u7ec4\u6240\u6709\u53ef\u4e3a null \u7c7b\u578b\u90fd\u53ef\u4ee5\u4f7f\u7528\u7684\u6210\u5458\u3002<\/p>\n<p>For example, you can programmatically discover whether the nullable variable indeed has been assigned a null value using the HasValue property or the != operator. The assigned value of a nullable type may be obtained directly or via the Value property. In fact, given that the ? suffix is just a shorthand for using Nullable<T>, you could implement your LocalNullableVariables() method as follows:<br \/>\n\u4f8b\u5982\uff0c\u53ef\u4ee5\u4f7f\u7528 HasValue \u5c5e\u6027\u6216 \uff01= \u8fd0\u7b97\u7b26\u4ee5\u7f16\u7a0b\u65b9\u5f0f\u53d1\u73b0\u662f\u5426\u786e\u5b9e\u4e3a\u53ef\u4e3a null \u53d8\u91cf\u5206\u914d\u4e86 null \u503c\u3002\u53ef\u4e3a null \u7c7b\u578b\u7684\u8d4b\u503c\u53ef\u4ee5\u76f4\u63a5\u83b7\u53d6\uff0c\u4e5f\u53ef\u4ee5\u901a\u8fc7 Value \u5c5e\u6027\u83b7\u53d6\u3002\u4e8b\u5b9e\u4e0a\uff0c\u9274\u4e8e \uff1f \u540e\u7f00\u53ea\u662f\u4f7f\u7528 Nullable<T> \u7684\u7b80\u5199\uff0c\u60a8\u53ef\u4ee5\u6309\u5982\u4e0b\u65b9\u5f0f\u5b9e\u73b0 LocalNullableVariables\uff08\uff09 \u65b9\u6cd5\uff1a<\/p>\n<p>static void LocalNullableVariablesUsingNullable()<br \/>\n{<br \/>\n\/\/ Define some local nullable types using Nullable<T>. Nullable<int> nullableInt = 10;<br \/>\nNullable<double> nullableDouble = 3.14; Nullable<bool> nullableBool = null; Nullable<char> nullableChar = 'a';<br \/>\nNullable<int>[] arrayOfNullableInts = new Nullable<int>[10];<br \/>\n}<\/p>\n<p>As stated, nullable data types can be particularly useful when you are interacting with databases, given that columns in a data table may be intentionally empty (e.g., undefined). To illustrate, assume the following class, which simulates the process of accessing a database that has a table containing two columns that<br \/>\nmay be null. Note that the GetIntFromDatabase() method is not assigning a value to the nullable integer member variable, while GetBoolFromDatabase() is assigning a valid value to the bool? member.<br \/>\n\u5982\u524d\u6240\u8ff0\uff0c\u5728\u4e0e\u6570\u636e\u5e93\u4ea4\u4e92\u65f6\uff0c\u53ef\u4e3a\u7a7a\u7684\u6570\u636e\u7c7b\u578b\u53ef\u80fd\u7279\u522b\u6709\u7528\uff0c\u56e0\u4e3a\u6570\u636e\u8868\u4e2d\u7684\u5217\u53ef\u80fd\u6709\u610f\u4e3a\u7a7a\uff08\u4f8b\u5982\uff0c\u672a\u5b9a\u4e49\uff09\u3002\u4e3a\u4e86\u8bf4\u660e\u8fd9\u4e00\u70b9\uff0c\u5047\u8bbe\u4ee5\u4e0b\u7c7b\uff0c\u8be5\u7c7b\u6a21\u62df\u8bbf\u95ee\u6570\u636e\u5e93\u7684\u8fc7\u7a0b\uff0c\u8be5\u6570\u636e\u5e93\u7684\u8868\u5305\u542b\u4e24\u5217\u53ef\u80fd\u4e3a\u7a7a\u3002\u8bf7\u6ce8\u610f\uff0cGetIntFromDatabase\uff08\uff09 \u65b9\u6cd5\u6ca1\u6709\u4e3a\u53ef\u4e3a\u7a7a\u7684\u6574\u6570\u6210\u5458\u53d8\u91cf\u8d4b\u503c\uff0c\u800c GetBoolFromDatabase\uff08\uff09 \u4e3a\u5e03\u5c14\u503c\u8d4b\u503c\uff1f \u6210\u5458\u3002<\/p>\n<p>class DatabaseReader<br \/>\n{<br \/>\n\/\/ Nullable data field.<br \/>\npublic int? numericValue = null; public bool? boolValue = true;<\/p>\n<p>\/\/ Note the nullable return type. public int? GetIntFromDatabase()<br \/>\n{ return numericValue; }<\/p>\n<p>\/\/ Note the nullable return type. public bool? GetBoolFromDatabase()<br \/>\n{ return boolValue; }<br \/>\n}<\/p>\n<p>Now, examine the following code, which invokes each member of the DatabaseReader class and discovers the assigned values using the HasValue and Value members, as well as using the C# equality operator (not equal, to be exact):<br \/>\n\u73b0\u5728\uff0c\u68c0\u67e5\u4ee5\u4e0b\u4ee3\u7801\uff0c\u8be5\u4ee3\u7801\u8c03\u7528 DatabaseReader \u7c7b\u7684\u6bcf\u4e2a\u6210\u5458\uff0c\u5e76\u4f7f\u7528 HasValue \u548c Value \u6210\u5458\u4ee5\u53ca C# \u76f8\u7b49\u8fd0\u7b97\u7b26\uff08\u786e\u5207\u5730\u8bf4\uff0c\u4e0d\u76f8\u7b49\uff09\u53d1\u73b0\u5206\u914d\u7684\u503c\uff1a<\/p>\n<p>Console.WriteLine(&quot;<strong><strong><em> Fun with Nullable Value Types <\/em><\/strong><\/strong>\\n&quot;); DatabaseReader dr = new DatabaseReader();<\/p>\n<p>\/\/ Get int from &quot;database&quot;.<br \/>\nint? i = dr.GetIntFromDatabase(); if (i.HasValue)<br \/>\n{<br \/>\nConsole.WriteLine(&quot;Value of 'i' is: {0}&quot;, i.Value);<br \/>\n}<br \/>\nelse<br \/>\n{<br \/>\nConsole.WriteLine(&quot;Value of 'i' is undefined.&quot;);<br \/>\n}<br \/>\n\/\/ Get bool from &quot;database&quot;.<br \/>\nbool? b = dr.GetBoolFromDatabase(); if (b != null)<br \/>\n{<br \/>\nConsole.WriteLine(&quot;Value of 'b' is: {0}&quot;, b.Value);<br \/>\n}<br \/>\nelse<br \/>\n{<br \/>\nConsole.WriteLine(&quot;Value of 'b' is undefined.&quot;);<br \/>\n}<br \/>\nConsole.ReadLine();<\/p>\n<h2>Using Nullable Reference Types (New 8.0, Updated 10.0)<\/h2>\n<p>\u4f7f\u7528\u53ef\u4e3a\u7a7a\u7684\u5f15\u7528\u7c7b\u578b\uff08\u65b0 8.0\uff0c\u66f4\u65b0\u7684 10.0\uff09<\/p>\n<p>A significant feature added with C# 8 is support for nullable reference types. In fact, the change is so significant that the .NET Framework could not be updated to support this new feature, which is one of the reasons for only supporting C# 8 in .NET Core 3.0 and above.<br \/>\nC# 8 \u6dfb\u52a0\u7684\u4e00\u9879\u91cd\u8981\u529f\u80fd\u662f\u652f\u6301\u53ef\u4e3a null \u7684\u5f15\u7528\u7c7b\u578b\u3002\u4e8b\u5b9e\u4e0a\uff0c\u6b64\u66f4\u6539\u662f\u5982\u6b64\u4e4b\u5927\uff0c\u4ee5\u81f3\u4e8e\u65e0\u6cd5\u66f4\u65b0 .NET Framework \u4ee5\u652f\u6301\u6b64\u65b0\u529f\u80fd\uff0c\u8fd9\u662f\u5728 .NET Core 8.3 \u53ca\u66f4\u9ad8\u7248\u672c\u4e2d\u4ec5\u652f\u6301 C# 0 \u7684\u539f\u56e0\u4e4b\u4e00\u3002<br \/>\nWhen you create a new project in .NET Core 3.0\/3.1 or .NET 5, reference types work the same way that they did with C# 7. This is to prevent breaking billions of lines of code that exist in the pre\u2013C# 8 ecosystem. Developers must opt in to enable nullable reference types in their applications.<br \/>\n\u5728 .NET Core 3.0\/3.1 \u6216 .NET 5 \u4e2d\u521b\u5efa\u65b0\u9879\u76ee\u65f6\uff0c\u5f15\u7528\u7c7b\u578b\u7684\u5de5\u4f5c\u65b9\u5f0f\u4e0e\u4f7f\u7528 C# 7 \u7684\u5de5\u4f5c\u65b9\u5f0f\u76f8\u540c\u3002\u8fd9\u662f\u4e3a\u4e86\u9632\u6b62\u7834\u574f C# 8 \u4e4b\u524d\u7684\u751f\u6001\u7cfb\u7edf\u4e2d\u5b58\u5728\u7684\u6570\u5341\u4ebf\u884c\u4ee3\u7801\u3002\u5f00\u53d1\u4eba\u5458\u5fc5\u987b\u9009\u62e9\u5728\u5176\u5e94\u7528\u7a0b\u5e8f\u4e2d\u542f\u7528\u53ef\u4e3a\u7a7a\u7684\u5f15\u7528\u7c7b\u578b\u3002<\/p>\n<p>C# 10 and .NET 6 (and above) change the default and enable nullable reference types in all of the project templates.<br \/>\nC# 10 \u548c .NET 6\uff08\u53ca\u66f4\u9ad8\u7248\u672c\uff09\u66f4\u6539\u9ed8\u8ba4\u503c\uff0c\u5e76\u5728\u6240\u6709\u9879\u76ee\u6a21\u677f\u4e2d\u542f\u7528\u53ef\u4e3a null \u7684\u5f15\u7528\u7c7b\u578b\u3002<\/p>\n<p>Nullable reference types follow many of the same rules as nullable value types. Non-nullable reference types must be assigned a non-null value at initialization and cannot later be changed to a null value.<br \/>\n\u53ef\u4e3a null \u7684\u5f15\u7528\u7c7b\u578b\u9075\u5faa\u8bb8\u591a\u4e0e\u53ef\u4e3a null \u7684\u503c\u7c7b\u578b\u76f8\u540c\u7684\u89c4\u5219\u3002\u4e0d\u53ef\u4e3a\u7a7a\u7684\u5f15\u7528\u7c7b\u578b\u5fc5\u987b\u5728\u521d\u59cb\u5316\u65f6\u5206\u914d\u975e\u7a7a\u503c\uff0c\u4ee5\u540e\u4e0d\u80fd\u66f4\u6539\u4e3a\u7a7a\u503c\u3002\u53ef\u4e3a null \u7684\u5f15\u7528\u7c7b\u578b\u53ef\u4ee5\u4e3a null\uff0c\u4f46\u5728\u9996\u6b21\u4f7f\u7528\u4e4b\u524d\u4ecd\u5fc5\u987b\u4e3a\u5176\u5206\u914d\u67d0\u4e9b\u5185\u5bb9\uff08\u67d0\u4e9b\u5185\u5bb9\u7684\u5b9e\u9645\u5b9e\u4f8b\u6216 null \u7684\u503c\uff09\u3002<\/p>\n<p>Nullable reference types can be null, but still must be assigned something before first use (either an actual instance of something or the value of null).<br \/>\nNullable reference types use the same symbol (?) to indicate that they are nullable. However, this is not a shorthand for using System.Nullable<T>, as only value types can be used in place of T. As a reminder, generics and constraints are covered in Chapter 10.<br \/>\n\u53ef\u4e3a\u7a7a\u7684\u5f15\u7528\u7c7b\u578b\u4f7f\u7528\u76f8\u540c\u7684\u7b26\u53f7 \uff08\uff1f\uff09 \u6765\u6307\u793a\u5b83\u4eec\u53ef\u4e3a\u7a7a\u3002\u4f46\u662f\uff0c\u8fd9\u4e0d\u662f\u4f7f\u7528 System.Nullable<T> \u7684\u7b80\u5199\uff0c\u56e0\u4e3a\u53ea\u80fd\u4f7f\u7528\u503c\u7c7b\u578b\u4ee3\u66ff T\u3002\u63d0\u9192\u4e00\u4e0b\uff0c\u7b2c10\u7ae0\u4ecb\u7ecd\u4e86\u6cdb\u578b\u548c\u7ea6\u675f\u3002<\/p>\n<h2>Opting in for Nullable Reference Types (Updated 10.0)<\/h2>\n<p>\u9009\u62e9\u52a0\u5165\u53ef\u4e3a\u7a7a\u7684\u5f15\u7528\u7c7b\u578b\uff0810.0 \u66f4\u65b0\uff09<br \/>\nSupport for nullable reference types is controlled by setting a nullable context. This can be as big as an entire project (by updating the project file) or as small as a few lines (by using compiler directives). There are also two contexts that can be set:<br \/>\n\u5bf9\u53ef\u4e3a\u7a7a\u7684\u5f15\u7528\u7c7b\u578b\u7684\u652f\u6301\u662f\u901a\u8fc7\u8bbe\u7f6e\u53ef\u4e3a\u7a7a\u7684\u4e0a\u4e0b\u6587\u6765\u63a7\u5236\u7684\u3002\u8fd9\u53ef\u4ee5\u5927\u5230\u6574\u4e2a\u9879\u76ee\uff08\u901a\u8fc7\u66f4\u65b0\u9879\u76ee\u6587\u4ef6\uff09\uff0c\u4e5f\u53ef\u4ee5\u5c0f\u5230\u51e0\u884c\uff08\u901a\u8fc7\u4f7f\u7528\u7f16\u8bd1\u5668\u6307\u4ee4\uff09\u3002\u8fd8\u53ef\u4ee5\u8bbe\u7f6e\u4e24\u4e2a\u4e0a\u4e0b\u6587\uff1a<\/p>\n<p>\u2022   Nullable annotation context: This enables\/disables the nullable annotation (?) for nullable reference types.<br \/>\n\u53ef\u4e3a\u7a7a\u7684\u6ce8\u91ca\u4e0a\u4e0b\u6587\uff1a\u8fd9\u5c06\u542f\u7528\/\u7981\u7528\u53ef\u4e3a\u7a7a\u7684\u5f15\u7528\u7c7b\u578b\u7684\u53ef\u4e3a\u7a7a\u7684\u6ce8\u91ca \uff08\uff1f\uff09\u3002<br \/>\n\u2022   Nullable warning context: This enables\/disables the compiler warnings for nullable reference types.<br \/>\n\u53ef\u4e3a\u7a7a\u7684\u8b66\u544a\u4e0a\u4e0b\u6587\uff1a\u8fd9\u5c06\u542f\u7528\/\u7981\u7528\u53ef\u4e3a\u7a7a\u7684\u5f15\u7528\u7c7b\u578b\u7684\u7f16\u8bd1\u5668\u8b66\u544a\u3002<\/p>\n<p>To see these in action, create a new console application named FunWithNullableReferenceTypes. Open the project file (if you are using Visual Studio, double-click the project name in Solution Explorer or right- click the project name and select Edit Project File). As mentioned previously, in C# 10, every new project defaults to enabling nullable reference types. Notice the <Nullable> node in the project file listing (all the available options are shown in Table 4-5):<br \/>\n\u82e5\u8981\u67e5\u770b\u8fd9\u4e9b\u64cd\u4f5c\uff0c\u8bf7\u521b\u5efa\u4e00\u4e2a\u540d\u4e3a FunWithNullableReferenceType \u7684\u65b0\u63a7\u5236\u53f0\u5e94\u7528\u7a0b\u5e8f\u3002\u6253\u5f00\u9879\u76ee\u6587\u4ef6\uff08\u5982\u679c\u4f7f\u7528\u7684\u662f Visual Studio\uff0c\u8bf7\u5728\u201c\u89e3\u51b3\u65b9\u6848\u8d44\u6e90\u7ba1\u7406\u5668\u201d\u4e2d\u53cc\u51fb\u9879\u76ee\u540d\u79f0\uff0c\u6216\u53f3\u952e\u5355\u51fb\u9879\u76ee\u540d\u79f0\u5e76\u9009\u62e9\u201c\u7f16\u8f91\u9879\u76ee\u6587\u4ef6\u201d\uff09\u3002\u5982\u524d\u6240\u8ff0\uff0c\u5728 C# 10 \u4e2d\uff0c\u6bcf\u4e2a\u65b0\u9879\u76ee\u9ed8\u8ba4\u542f\u7528\u53ef\u4e3a null \u7684\u5f15\u7528\u7c7b\u578b\u3002\u8bf7\u6ce8\u610f\u9879\u76ee\u6587\u4ef6\u5217\u8868\u4e2d\u7684&lt;\u53ef\u4e3a\u7a7a&gt;\u8282\u70b9\uff08\u8868 4-5 \u4e2d\u663e\u793a\u4e86\u6240\u6709\u53ef\u7528\u9009\u9879\uff09\uff1a<\/p>\n<p><pre class=\"brush: xml; title: ; notranslate\" title=\"\">\n&lt;Project Sdk=&quot;Microsoft.NET.Sdk&quot;&gt;\n    &lt;PropertyGroup&gt;\n    &lt;OutputType&gt;Exe&lt;\/OutputType&gt;\n    &lt;TargetFramework&gt;net6.0&lt;\/TargetFramework&gt;\n    &lt;ImplicitUsings&gt;enable&lt;\/ImplicitUsings&gt;\n    &lt;Nullable&gt;enable&lt;\/Nullable&gt;\n    &lt;\/PropertyGroup&gt;\n&lt;\/Project&gt;\n<\/pre>\n<\/p>\n<p>Table 4-5. Values for Nullable in Project Files<br \/>\n\u8868 4-5. \u9879\u76ee\u6587\u4ef6\u4e2d\u53ef\u4e3a\u7a7a\u7684\u503c<\/p>\n<table>\n<tr>\n<td>Value<\/td>\n<td>Meaning in Life<\/td>\n<\/tr>\n<tr>\n<td>enable<\/td>\n<td>Nullable annotations are enabled, and nullable warnings are enabled.<br \/>\n        \u542f\u7528\u53ef\u4e3a\u7a7a\u7684\u6279\u6ce8\uff0c\u5e76\u542f\u7528\u53ef\u4e3a\u7a7a\u7684\u8b66\u544a\u3002<\/td>\n<\/tr>\n<tr>\n<td>warnings<\/td>\n<td>Nullable annotations are disabled, and nullable warnings are enabled.<br \/>\n        \u7981\u7528\u53ef\u4e3a\u7a7a\u7684\u6279\u6ce8\uff0c\u5e76\u542f\u7528\u53ef\u4e3a\u7a7a\u7684\u8b66\u544a\u3002<\/td>\n<\/tr>\n<tr>\n<td>annotations<\/td>\n<td>Nullable annotations are enabled, and nullable warnings are disabled.<br \/>\n        \u542f\u7528\u53ef\u4e3a\u7a7a\u7684\u6279\u6ce8\uff0c\u7981\u7528\u53ef\u4e3a\u7a7a\u7684\u8b66\u544a\u3002<\/td>\n<\/tr>\n<tr>\n<td>disable<\/td>\n<td>Nullable annotations are disabled, and nullable warnings are disabled.<br \/>\n        \u7981\u7528\u53ef\u4e3a\u7a7a\u7684\u6279\u6ce8\uff0c\u5e76\u7981\u7528\u53ef\u4e3a\u7a7a\u7684\u8b66\u544a\u3002<\/td>\n<\/tr>\n<\/table>\n<p>As one would expect, the <Nullable> element in the project file affects the entire project. To control smaller parts of the project, use the compiler directives shown in Table 4-6.<br \/>\n\u6b63\u5982\u4eba\u4eec\u6240\u671f\u671b\u7684\u90a3\u6837\uff0c\u9879\u76ee\u6587\u4ef6\u4e2d\u7684&lt;\u53ef\u4e3a\u7a7a&gt;\u5143\u7d20\u4f1a\u5f71\u54cd\u6574\u4e2a\u9879\u76ee\u3002\u82e5\u8981\u63a7\u5236\u9879\u76ee\u7684\u8f83\u5c0f\u90e8\u5206\uff0c\u8bf7\u4f7f\u7528\u8868 4-6 \u4e2d\u6240\u793a\u7684\u7f16\u8bd1\u5668\u6307\u4ee4\u3002<\/p>\n<p>Table 4-6. Values for #nullable Compiler Directive<br \/>\n\u8868 4-6. #nullable \u7f16\u8bd1\u5668\u6307\u4ee4\u7684\u503c<\/p>\n<table>\n<tr>\n<td>Value<\/td>\n<td>Meaning in Life<\/td>\n<\/tr>\n<tr>\n<td>enable<\/td>\n<td>Annotations are enabled, and warnings are enabled.<br \/>\n        \u542f\u7528\u6279\u6ce8\uff0c\u5e76\u542f\u7528\u8b66\u544a\u3002<\/td>\n<\/tr>\n<tr>\n<td>disable<\/td>\n<td>Annotations are disabled, and warnings are disabled.<br \/>\n        \u7981\u7528\u6279\u6ce8\uff0c\u5e76\u7981\u7528\u8b66\u544a\u3002<\/td>\n<\/tr>\n<tr>\n<td>restore<\/td>\n<td>Restores all settings to the project settings.<br \/>\n        \u5c06\u6240\u6709\u8bbe\u7f6e\u8fd8\u539f\u4e3a\u9879\u76ee\u8bbe\u7f6e\u3002<\/td>\n<\/tr>\n<tr>\n<td>disable warnings<\/td>\n<td>Warnings are disabled, and annotations are unaffected.<br \/>\n        \u8b66\u544a\u88ab\u7981\u7528\uff0c\u6279\u6ce8\u4e0d\u53d7\u5f71\u54cd\u3002<\/td>\n<\/tr>\n<tr>\n<td>enable warnings<\/td>\n<td>Warnings are enabled, and annotations are unaffected.<br \/>\n        \u8b66\u544a\u5df2\u542f\u7528\uff0c\u6279\u6ce8\u4e0d\u53d7\u5f71\u54cd\u3002<\/td>\n<\/tr>\n<tr>\n<td>restore warnings<\/td>\n<td>Warnings reset to project settings; annotations are unaffected.<br \/>\n        \u8b66\u544a\u91cd\u7f6e\u4e3a\u9879\u76ee\u8bbe\u7f6e;\u6279\u6ce8\u4e0d\u53d7\u5f71\u54cd\u3002<\/td>\n<\/tr>\n<tr>\n<td>disable annotations<\/td>\n<td>Annotations are disabled, and warnings are unaffected.<br \/>\n        \u6ce8\u91ca\u88ab\u7981\u7528\uff0c\u8b66\u544a\u4e0d\u53d7\u5f71\u54cd\u3002<\/td>\n<\/tr>\n<tr>\n<td>enable annotations<\/td>\n<td>Annotations are enabled, and warnings are unaffected.<br \/>\n        \u6279\u6ce8\u5df2\u542f\u7528\uff0c\u8b66\u544a\u4e0d\u53d7\u5f71\u54cd\u3002<\/td>\n<\/tr>\n<tr>\n<td>restore annotations<\/td>\n<td>Annotations are reset to project settings; warnings are unaffected.<br \/>\n        \u6ce8\u91ca\u5c06\u91cd\u7f6e\u4e3a\u9879\u76ee\u8bbe\u7f6e;\u8b66\u544a\u4e0d\u53d7\u5f71\u54cd\u3002<\/td>\n<\/tr>\n<\/table>\n<p>\u25a0   Note as mentioned, with the introduction of C# 10\/.net 6, nullable reference types (nrts) are enabled by default. For the rest of this book, the code samples all have nrts disabled unless specifically called out in the example. this is not to say you shouldn\u2019t use nrts\u2014that is a decision you need to make based on your project\u2019s requirements. i have disabled them to keep the examples in this book focused on the specific teaching goal.<br \/>\n\u6ce8\u610f \u5982\u524d\u6240\u8ff0\uff0c\u968f\u7740 C# 10\/.net 6 \u7684\u5f15\u5165\uff0c\u9ed8\u8ba4\u60c5\u51b5\u4e0b\u542f\u7528\u53ef\u4e3a\u7a7a\u7684\u5f15\u7528\u7c7b\u578b \uff08nrt\uff09\u3002\u5bf9\u4e8e\u672c\u4e66\u7684\u5176\u4f59\u90e8\u5206\uff0c\u9664\u975e\u793a\u4f8b\u4e2d\u7279\u522b\u6307\u51fa\uff0c\u5426\u5219\u4ee3\u7801\u793a\u4f8b\u90fd\u7981\u7528\u4e86 nrt\u3002\u8fd9\u5e76\u4e0d\u662f\u8bf4\u60a8\u4e0d\u5e94\u8be5\u4f7f\u7528 NRT\uff0c\u8fd9\u662f\u60a8\u9700\u8981\u6839\u636e\u9879\u76ee\u8981\u6c42\u505a\u51fa\u7684\u51b3\u5b9a\u3002\u6211\u7981\u7528\u4e86\u5b83\u4eec\uff0c\u4ee5\u4f7f\u672c\u4e66\u4e2d\u7684\u793a\u4f8b\u4e13\u6ce8\u4e8e\u7279\u5b9a\u7684\u6559\u5b66\u76ee\u6807\u3002<\/p>\n<h2>Nullable Reference Types in Action<\/h2>\n<p>\u64cd\u4f5c\u4e2d\u7684\u53ef\u4e3a\u7a7a\u7684\u5f15\u7528\u7c7b\u578b<\/p>\n<p>Largely because of the significance of the change, nullable types only throw errors when used improperly. Add the following class to the Program.cs file:<br \/>\n\u5f88\u5927\u7a0b\u5ea6\u4e0a\u7531\u4e8e\u66f4\u6539\u7684\u91cd\u8981\u6027\uff0c\u53ef\u4e3a null \u7684\u7c7b\u578b\u4ec5\u5728\u4f7f\u7528\u4e0d\u5f53\u65f6\u5f15\u53d1\u9519\u8bef\u3002\u5c06\u4ee5\u4e0b\u7c7b\u6dfb\u52a0\u5230\u7a0b\u5e8f.cs\u6587\u4ef6\u4e2d\uff1a<\/p>\n<p>public class TestClass<br \/>\n{<br \/>\npublic string Name { get; set; } public int Age { get; set; }<br \/>\n}<\/p>\n<p>As you can see, this is just a normal class. The nullability comes in when you use this class in your code.<br \/>\n\u5982\u60a8\u6240\u89c1\uff0c\u8fd9\u53ea\u662f\u4e00\u4e2a\u666e\u901a\u7684\u8bfe\u7a0b\u3002\u5728\u4ee3\u7801\u4e2d\u4f7f\u7528\u6b64\u7c7b\u65f6\uff0c\u53ef\u7a7a\u6027\u5c31\u4f1a\u51fa\u73b0\u3002<\/p>\n<p>Take the following declarations:<br \/>\n\u91c7\u53d6\u4ee5\u4e0b\u58f0\u660e\uff1a<\/p>\n<p>string? nullableString = null; TestClass? myNullableClass = null;<\/p>\n<p>The project file setting makes the entire project a nullable context. The nullable context allows the declarations of the string and TestClass types to use the nullable annotation (?). The following line of code generates a warning (CS8600) due to the assignment of a null to a non-nullable type in a nullable context:<br \/>\n\u9879\u76ee\u6587\u4ef6\u8bbe\u7f6e\u4f7f\u6574\u4e2a\u9879\u76ee\u6210\u4e3a\u53ef\u4e3a\u7a7a\u7684\u4e0a\u4e0b\u6587\u3002\u53ef\u4e3a\u7a7a\u7684\u4e0a\u4e0b\u6587\u5141\u8bb8\u5b57\u7b26\u4e32\u548c TestClass \u7c7b\u578b\u7684\u58f0\u660e\u4f7f\u7528\u53ef\u4e3a\u7a7a\u7684\u6ce8\u91ca \uff08\uff1f\uff09\u3002\u4ee5\u4e0b\u4ee3\u7801\u884c\u751f\u6210\u8b66\u544a \uff08CS8600\uff09\uff0c\u539f\u56e0\u662f\u5728\u53ef\u4e3a null \u7684\u4e0a\u4e0b\u6587\u4e2d\u5c06 null \u5206\u914d\u7ed9\u4e0d\u53ef\u4e3a\u7a7a\u7684\u7c7b\u578b\uff1a<\/p>\n<p>\/\/Warning CS8600 Converting null literal or possible null value to non-nullable type TestClass myNonNullableClass = myNullableClass;<\/p>\n<p>For finer control of where the nullable contexts are in your project, you can use compiler directives (as discussed earlier) to enable or disable the context. The following code turns off the nullable context (set at the project level) and then reenables it by restoring the project settings:<br \/>\n\u4e3a\u4e86\u66f4\u597d\u5730\u63a7\u5236\u53ef\u4e3a null \u7684\u4e0a\u4e0b\u6587\u5728\u9879\u76ee\u4e2d\u7684\u4f4d\u7f6e\uff0c\u53ef\u4ee5\u4f7f\u7528\u7f16\u8bd1\u5668\u6307\u4ee4\uff08\u5982\u524d\u6240\u8ff0\uff09\u6765\u542f\u7528\u6216\u7981\u7528\u4e0a\u4e0b\u6587\u3002\u4ee5\u4e0b\u4ee3\u7801\u5173\u95ed\u53ef\u4e3a null \u7684\u4e0a\u4e0b\u6587\uff08\u5728\u9879\u76ee\u7ea7\u522b\u8bbe\u7f6e\uff09\uff0c\u7136\u540e\u901a\u8fc7\u8fd8\u539f\u9879\u76ee\u8bbe\u7f6e\u91cd\u65b0\u542f\u7528\u5b83\uff1a<\/p>\n<h1>nullable disable<\/h1>\n<p>TestClass anotherNullableClass = null;<br \/>\n\/\/Warning CS8632 The annotation for nullable reference types<br \/>\n\/\/should only be used in code within a '#nullable' annotations<\/p>\n<p>TestClass? badDefinition = null;<br \/>\n\/\/Warning CS8632 The annotation for nullable reference types<br \/>\n\/\/should only be used in code within a '#nullable' annotations string? anotherNullableString = null;<\/p>\n<h1>nullable restore<\/h1>\n<p>Add the previous example method EnterLogData() into the top-level statements of our current project:<br \/>\n\u5c06\u524d\u9762\u7684\u793a\u4f8b\u65b9\u6cd5 EnterLogData\uff08\uff09 \u6dfb\u52a0\u5230\u6211\u4eec\u5f53\u524d\u9879\u76ee\u7684\u9876\u7ea7\u8bed\u53e5\u4e2d\uff1a<\/p>\n<p>static void EnterLogData(string message, string owner = &quot;Programmer&quot;)<br \/>\n{<br \/>\nArgumentNullException.ThrowIfNull(message); Console.WriteLine(&quot;Error: {0}&quot;, message); Console.WriteLine(&quot;Owner of Error: {0}&quot;, owner);<br \/>\n}<\/p>\n<p>Since this project has nullable reference types enabled, the message and owner parameters are not nullable. The owner parameter has a default value set, so it will never be null. However, the message parameter does not have a default value set, so calling it like this will raise a compiler warning:<br \/>\n\u7531\u4e8e\u6b64\u9879\u76ee\u542f\u7528\u4e86\u53ef\u4e3a\u7a7a\u7684\u5f15\u7528\u7c7b\u578b\uff0c\u56e0\u6b64\u6d88\u606f\u548c\u6240\u6709\u8005\u53c2\u6570\u4e0d\u53ef\u4e3a\u7a7a\u3002owner \u53c2\u6570\u8bbe\u7f6e\u4e86\u9ed8\u8ba4\u503c\uff0c\u56e0\u6b64\u5b83\u6c38\u8fdc\u4e0d\u4f1a\u4e3a null\u3002\u4f46\u662f\uff0c\u6d88\u606f\u53c2\u6570\u6ca1\u6709\u8bbe\u7f6e\u9ed8\u8ba4\u503c\uff0c\u56e0\u6b64\u50cf\u8fd9\u6837\u8c03\u7528\u5b83\u5c06\u5f15\u53d1\u7f16\u8bd1\u5668\u8b66\u544a\uff1a<\/p>\n<p>EnterLogData(null);<br \/>\n\/\/Warning CS8625 Cannot convert null literal to non-nullable reference type.<\/p>\n<p>Chances are you won\u2019t explicitly pass in a null value, but more likely you might pass in a variable that happens to be null:<br \/>\n\u60a8\u53ef\u80fd\u4e0d\u4f1a\u663e\u5f0f\u4f20\u5165 null \u503c\uff0c\u4f46\u66f4\u6709\u53ef\u80fd\u4f20\u5165\u6070\u597d\u4e3a null \u7684\u53d8\u91cf\uff1a<\/p>\n<p>string? msg = null;<br \/>\nEnterLogData(msg);<br \/>\n\/\/Warning CS8604 Possible null reference argument for parameter 'message' in<br \/>\n\/\/  'void EnterLogData(string message, string owner = &quot;Programmer&quot;)'.<\/p>\n<p>This doesn\u2019t solve the problem of null values passed into a method, but it does provide some level of compile-time checking of your code.<br \/>\n\u8fd9\u5e76\u4e0d\u80fd\u89e3\u51b3\u5c06 null \u503c\u4f20\u9012\u5230\u65b9\u6cd5\u4e2d\u7684\u95ee\u9898\uff0c\u4f46\u5b83\u786e\u5b9e\u63d0\u4f9b\u4e86\u4e00\u5b9a\u7a0b\u5ea6\u7684\u4ee3\u7801\u7f16\u8bd1\u65f6\u68c0\u67e5\u3002<br \/>\nAs a final note, the nullable reference types do not have the HasValue and Value properties, as those are supplied by System.Nullable<T>.<br \/>\n\u6700\u540e\uff0c\u53ef\u4e3a\u7a7a\u7684\u5f15\u7528\u7c7b\u578b\u6ca1\u6709 HasValue \u548c Value \u5c5e\u6027\uff0c\u56e0\u4e3a\u5b83\u4eec\u7531 System.Nullable<T> \u63d0\u4f9b\u3002<\/p>\n<h2>Migration Considerations<\/h2>\n<p>\u8fc1\u79fb\u6ce8\u610f\u4e8b\u9879<\/p>\n<p>When migrating your code from C# 7 to C# 8, C# 9, or C# 10 and you want to make use of nullable reference types, you can use a combination of the project setting and compiler directives to work through your code.<br \/>\n\u5c06\u4ee3\u7801\u4ece C# 7 \u8fc1\u79fb\u5230 C# 8\u3001C# 9 \u6216 C# 10 \u65f6\uff0c\u5982\u679c\u60f3\u8981\u4f7f\u7528\u53ef\u4e3a null \u7684\u5f15\u7528\u7c7b\u578b\uff0c\u5219\u53ef\u4ee5\u7ed3\u5408\u4f7f\u7528\u9879\u76ee\u8bbe\u7f6e\u548c\u7f16\u8bd1\u5668\u6307\u4ee4\u6765\u5b8c\u6210\u4ee3\u7801\u3002<\/p>\n<p>A common practice is to start by enabling warnings and disabling nullable annotations for the entire project. Then, as you clean up areas of code, use the compiler directives to gradually enable the annotations. Remember that with C# 10, nullable reference types are enabled by default in project templates.<br \/>\n\u4e00\u79cd\u5e38\u89c1\u7684\u505a\u6cd5\u662f\u4ece\u542f\u7528\u8b66\u544a\u5e76\u7981\u7528\u6574\u4e2a\u9879\u76ee\u7684\u53ef\u4e3a\u7a7a\u7684\u6279\u6ce8\u5f00\u59cb\u3002\u7136\u540e\uff0c\u5728\u6e05\u7406\u4ee3\u7801\u533a\u57df\u65f6\uff0c\u4f7f\u7528\u7f16\u8bd1\u5668\u6307\u4ee4\u9010\u6b65\u542f\u7528\u6279\u6ce8\u3002\u8bf7\u8bb0\u4f4f\uff0c\u5728 C# 10 \u4e2d\uff0c\u9ed8\u8ba4\u60c5\u51b5\u4e0b\u5728\u9879\u76ee\u6a21\u677f\u4e2d\u542f\u7528\u53ef\u4e3a null \u7684\u5f15\u7528\u7c7b\u578b\u3002<\/p>\n<h2>Change Nullable Warnings to Errors<\/h2>\n<p>\u5c06\u53ef\u4e3a\u7a7a\u7684\u8b66\u544a\u66f4\u6539\u4e3a\u9519\u8bef<\/p>\n<p>When you are ready to commit to nullable reference types, you can configure the nullable warnings as errors. The easiest way to do this for a project is to add the following into the project file:<br \/>\n\u51c6\u5907\u597d\u63d0\u4ea4\u53ef\u4e3a\u7a7a\u7684\u5f15\u7528\u7c7b\u578b\u65f6\uff0c\u53ef\u4ee5\u5c06\u53ef\u4e3a\u7a7a\u7684\u8b66\u544a\u914d\u7f6e\u4e3a\u9519\u8bef\u3002\u4e3a\u9879\u76ee\u6267\u884c\u6b64\u64cd\u4f5c\u7684\u6700\u7b80\u5355\u65b9\u6cd5\u662f\u5c06\u4ee5\u4e0b\u5185\u5bb9\u6dfb\u52a0\u5230\u9879\u76ee\u6587\u4ef6\u4e2d\uff1a<\/p>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\n&lt;PropertyGroup&gt;\n    &lt;WarningsAsErrors&gt;CS8604,CS8625&lt;\/WarningsAsErrors&gt;\n&lt;\/PropertyGroup&gt;\n<\/pre>\n<\/p>\n<p>In fact, if you want to treat all warnings related to nullable reference types to errors, you can use the following syntax:<br \/>\n\u5b9e\u9645\u4e0a\uff0c\u5982\u679c\u8981\u5c06\u4e0e\u53ef\u4e3a null \u7684\u5f15\u7528\u7c7b\u578b\u76f8\u5173\u7684\u6240\u6709\u8b66\u544a\u5904\u7406\u4e3a\u9519\u8bef\uff0c\u5219\u53ef\u4ee5\u4f7f\u7528\u4ee5\u4e0b\u8bed\u6cd5\uff1a<\/p>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\n&lt;PropertyGroup&gt;\n    &lt;WarningsAsErrors&gt;Nullable&lt;\/WarningsAsErrors&gt;\n&lt;\/PropertyGroup&gt;\n<\/pre>\n<p>\u25a0   Note You can change the severity of any warning to an error, not just those regarding nullable reference types. You can also change all warnings to errors by using <TreatWarningsAsErrors>true&lt;\/ TreatWarningsAsErrors&gt; instead of the WarningsAsErrors node in your project file.<br \/>\n\u6ce8\u610f \u53ef\u4ee5\u5c06\u4efb\u4f55\u8b66\u544a\u7684\u4e25\u91cd\u6027\u66f4\u6539\u4e3a\u9519\u8bef\uff0c\u800c\u4e0d\u4ec5\u4ec5\u662f\u6709\u5173\u53ef\u4e3a\u7a7a\u7684\u5f15\u7528\u7c7b\u578b\u7684\u8b66\u544a\u3002\u8fd8\u53ef\u4ee5\u901a\u8fc7\u4f7f\u7528\u9879\u76ee\u6587\u4ef6\u4e2d\u7684 <TreatWarningsAsErrors>true&lt;\/ TreatWarningsAsErrors&gt;\u800c\u4e0d\u662f WarningsAsErrors \u8282\u70b9\u5c06\u6240\u6709\u8b66\u544a\u66f4\u6539\u4e3a\u9519\u8bef\u3002<\/p>\n<h2>Operating on Nullable Types<\/h2>\n<p>\u5bf9\u53ef\u4e3a\u7a7a\u7c7b\u578b\u8fdb\u884c\u64cd\u4f5c<\/p>\n<p>C# provides several operators for working with nullable types. The next sections code the null-coalescing operator, the null-coalescing assignment operator, and the null conditional operator. For these examples, go back to the FunWithNullableValueTypes project.<br \/>\nC# \u63d0\u4f9b\u4e86\u591a\u4e2a\u8fd0\u7b97\u7b26\u6765\u5904\u7406\u53ef\u4e3a null \u7684\u7c7b\u578b\u3002\u63a5\u4e0b\u6765\u7684\u90e8\u5206\u5bf9 null \u5408\u5e76\u8fd0\u7b97\u7b26\u3001null \u5408\u5e76\u8d4b\u503c\u8fd0\u7b97\u7b26\u548c null \u6761\u4ef6\u8fd0\u7b97\u7b26\u8fdb\u884c\u7f16\u7801\u3002\u5bf9\u4e8e\u8fd9\u4e9b\u793a\u4f8b\uff0c\u8bf7\u8fd4\u56de\u5230 FunWithNullableValueTypes \u9879\u76ee\u3002<\/p>\n<h2>The Null-Coalescing Operator<\/h2>\n<p>\u96f6\u5408\u5e76\u8fd0\u7b97\u7b26<\/p>\n<p>The next aspect to be aware of is any variable that might have a null value can make use of the C# ?? operator, which is formally termed the null-coalescing operator. This operator allows you to assign a value to a nullable type if the retrieved value is in fact null. For this example, assume you want to assign a local nullable integer to 100 if the value returned from GetIntFromDatabase() is null (of course, this method is programmed to always return null, but I am sure you get the general idea). Move back to the FunWithNullableValueTypes project (and set it as the startup project), and enter the following code:<br \/>\n\u4e0b\u4e00\u4e2a\u9700\u8981\u6ce8\u610f\u7684\u65b9\u9762\u662f\u4efb\u4f55\u53ef\u80fd\u5177\u6709\u7a7a\u503c\u7684\u53d8\u91cf\u90fd\u53ef\u4ee5\u4f7f\u7528 C#\uff1f \u8fd0\u7b97\u7b26\uff0c\u6b63\u5f0f\u540d\u79f0\u4e3a\u96f6\u5408\u5e76\u8fd0\u7b97\u7b26\u3002\u5982\u679c\u68c0\u7d22\u5230\u7684\u503c\u5b9e\u9645\u4e0a\u662f null\uff0c\u5219\u6b64\u8fd0\u7b97\u7b26\u5141\u8bb8\u60a8\u5c06\u503c\u5206\u914d\u7ed9\u53ef\u4e3a null \u7684\u7c7b\u578b\u3002\u5bf9\u4e8e\u6b64\u793a\u4f8b\uff0c\u5047\u8bbe\u5982\u679c\u4ece GetIntFromDatabase\uff08\uff09 \u8fd4\u56de\u7684\u503c\u4e3a null\uff0c\u5219\u8981\u5c06\u672c\u5730\u53ef\u4e3a null \u7684\u6574\u6570\u5206\u914d\u7ed9 100\uff08\u5f53\u7136\uff0c\u6b64\u65b9\u6cd5\u88ab\u7f16\u7a0b\u4e3a\u59cb\u7ec8\u8fd4\u56de null\uff0c\u4f46\u6211\u76f8\u4fe1\u60a8\u4e86\u89e3\u5927\u81f4\u60f3\u6cd5\uff09\u3002\u79fb\u56de FunWithNullableValueTypes \u9879\u76ee\uff08\u5e76\u5c06\u5176\u8bbe\u7f6e\u4e3a\u542f\u52a8\u9879\u76ee\uff09\uff0c\u7136\u540e\u8f93\u5165\u4ee5\u4e0b\u4ee3\u7801\uff1a<\/p>\n<p>Console.WriteLine(&quot;<strong><strong><em> Fun with Nullable Value Types <\/em><\/strong><\/strong>\\n&quot;);<br \/>\nDatabaseReader dr = new DatabaseReader();<\/p>\n<p>\/\/ If the value from GetIntFromDatabase() is null,<br \/>\n\/\/ assign local variable to 100.<br \/>\nint myData = dr.GetIntFromDatabase() ?? 100; Console.WriteLine(&quot;Value of myData: {0}&quot;, myData); Console.ReadLine();<\/p>\n<p>The benefit of using the ?? operator is that it provides a more compact version of a traditional if\/else condition. However, if you want, you could have authored the following functionally equivalent code to ensure that if a value comes back as null, it will indeed be set to the value 100:<br \/>\n\u4f7f\u7528 \uff1f\uff1f \u8fd0\u7b97\u7b26\u662f\u5b83\u63d0\u4f9b\u4e86\u4f20\u7edf IF\/ELSE \u6761\u4ef6\u7684\u66f4\u7d27\u51d1\u7248\u672c\u3002\u4f46\u662f\uff0c\u5982\u679c\u9700\u8981\uff0c\u53ef\u4ee5\u7f16\u5199\u4ee5\u4e0b\u529f\u80fd\u7b49\u6548\u7684\u4ee3\u7801\uff0c\u4ee5\u786e\u4fdd\u5982\u679c\u503c\u8fd4\u56de\u4e3a null\uff0c\u5b83\u786e\u5b9e\u4f1a\u8bbe\u7f6e\u4e3a\u503c 100\uff1a<\/p>\n<p>\/\/ Longhand notation not using ?? syntax. int? moreData = dr.GetIntFromDatabase(); if (!moreData.HasValue)<br \/>\n{<br \/>\nmoreData = 100;<br \/>\n}<br \/>\nConsole.WriteLine(&quot;Value of moreData: {0}&quot;, moreData);<\/p>\n<h2>The Null-Coalescing Assignment Operator (New 8.0)<\/h2>\n<p>\u7a7a\u5408\u5e76\u8d4b\u503c\u8fd0\u7b97\u7b26\uff08\u65b0\u7248 8.0\uff09<\/p>\n<p>Building on the null-coalescing operator, C# 8 introduced the null-coalescing assignment operator (??=). This operator assigns the left-hand side to the right-hand side only if the left-hand side is null. For example, enter the following code:<br \/>\n\u5728\u96f6\u5408\u5e76\u8fd0\u7b97\u7b26\u7684\u57fa\u7840\u4e0a\uff0cC# 8 \u5f15\u5165\u4e86\u96f6\u5408\u5e76\u8d4b\u503c\u8fd0\u7b97\u7b26 \uff08\uff1f\uff1f=).\u4ec5\u5f53\u5de6\u4fa7\u4e3a null \u65f6\uff0c\u6b64\u8fd0\u7b97\u7b26\u624d\u4f1a\u5c06\u5de6\u4fa7\u5206\u914d\u7ed9\u53f3\u4fa7\u3002\u4f8b\u5982\uff0c\u8f93\u5165\u4ee5\u4e0b\u4ee3\u7801\uff1a<\/p>\n<p>\/\/Null-coalescing assignment operator int? nullableInt = null;<br \/>\nnullableInt ??= 12;<br \/>\nnullableInt ??= 14; Console.WriteLine(nullableInt);<\/p>\n<p>The nullableInt variable is initialized to null. The next line assigns the value of 12 to the variable since the left-hand side is indeed null. The next line does not assign 14 to the variable since it is not null.<br \/>\nnullableInt \u53d8\u91cf\u521d\u59cb\u5316\u4e3a null\u3002\u4e0b\u4e00\u884c\u5c06\u503c 12 \u5206\u914d\u7ed9\u53d8\u91cf\uff0c\u56e0\u4e3a\u5de6\u4fa7\u786e\u5b9e\u4e3a null\u3002\u4e0b\u4e00\u884c\u4e0d\u4f1a\u4e3a\u53d8\u91cf\u8d4b\u503c 14\uff0c\u56e0\u4e3a\u5b83\u4e0d\u4e3a null\u3002<\/p>\n<h2>The Null Conditional Operator<\/h2>\n<p>\u7a7a\u6761\u4ef6\u8fd0\u7b97\u7b26<\/p>\n<p>When you are writing software, it is common to check incoming parameters, which are values returned from type members (methods, properties, indexers), against the value null. For example, let\u2019s assume you have a method that takes a string array as a single parameter. To be safe, you might want to test for null before proceeding. In that way, you will not get a runtime error if the array is empty. The following would be a traditional way to perform such a check:<br \/>\n\u7f16\u5199\u8f6f\u4ef6\u65f6\uff0c\u901a\u5e38\u4f1a\u6839\u636e\u503c null \u68c0\u67e5\u4f20\u5165\u53c2\u6570\uff0c\u8fd9\u4e9b\u53c2\u6570\u662f\u4ece\u7c7b\u578b\u6210\u5458\uff08\u65b9\u6cd5\u3001\u5c5e\u6027\u3001\u7d22\u5f15\u5668\uff09\u8fd4\u56de\u7684\u503c\u3002\u4f8b\u5982\uff0c\u5047\u8bbe\u4f60\u6709\u5c06\u5b57\u7b26\u4e32\u6570\u7ec4\u4f5c\u4e3a\u5355\u4e2a\u53c2\u6570\u7684\u65b9\u6cd5\u3002\u4e3a\u4e86\u5b89\u5168\u8d77\u89c1\uff0c\u60a8\u53ef\u80fd\u9700\u8981\u5728\u4e4b\u524d\u6d4b\u8bd5\u7a7a\u503c\u8fdb\u884c\u4e2d\u3002\u8fd9\u6837\uff0c\u5982\u679c\u6570\u7ec4\u4e3a\u7a7a\uff0c\u5219\u4e0d\u4f1a\u6536\u5230\u8fd0\u884c\u65f6\u9519\u8bef\u3002\u4ee5\u4e0b\u662f\u6267\u884c\u6b64\u7c7b\u68c0\u67e5\u7684\u4f20\u7edf\u65b9\u6cd5\uff1a<\/p>\n<p>static void TesterMethod(string[] args)<br \/>\n{<br \/>\n\/\/ We should check for null before accessing the array data!<br \/>\n\/\/ \u6211\u4eec\u5e94\u8be5\u5728\u8bbf\u95ee\u6570\u7ec4\u6570\u636e\u4e4b\u524d\u68c0\u67e5 null\uff01<br \/>\nif (args != null)<br \/>\n{<br \/>\nConsole.WriteLine($&quot;You sent me {args.Length} arguments.&quot;);<br \/>\n}<br \/>\n}<\/p>\n<p>Here, you use a conditional scope to ensure that the Length property of the string array will not be accessed if the array is null. If the caller failed to make an array of data and called your method like so, you are still safe and will not trigger a runtime error:<br \/>\n\u5728\u8fd9\u91cc\uff0c\u60a8\u4f7f\u7528\u6761\u4ef6\u4f5c\u7528\u57df\u6765\u786e\u4fdd\u5982\u679c\u6570\u7ec4\u4e3a null\uff0c\u5219\u4e0d\u4f1a\u8bbf\u95ee\u5b57\u7b26\u4e32\u6570\u7ec4\u7684 Length \u5c5e\u6027\u3002\u5982\u679c\u8c03\u7528\u65b9\u672a\u80fd\u521b\u5efa\u6570\u636e\u6570\u7ec4\u5e76\u50cf\u8fd9\u6837\u8c03\u7528\u60a8\u7684\u65b9\u6cd5\uff0c\u60a8\u4ecd\u7136\u662f\u5b89\u5168\u7684\uff0c\u5e76\u4e14\u4e0d\u4f1a\u89e6\u53d1\u8fd0\u884c\u65f6\u9519\u8bef\uff1a<\/p>\n<p>TesterMethod(null);<\/p>\n<p>C# includes the null conditional operator token (a question mark placed after a variable type but before an access operator) to simplify the previous error checking. Rather than explicitly building a conditional statement to check for null, you can now write the following:<br \/>\nC# \u5305\u542b null \u6761\u4ef6\u8fd0\u7b97\u7b26\u6807\u8bb0\uff08\u4f4d\u4e8e\u53d8\u91cf\u7c7b\u578b\u4e4b\u540e\u4f46\u8bbf\u95ee\u8fd0\u7b97\u7b26\u4e4b\u524d\u7684\u95ee\u53f7\uff09\uff0c\u4ee5\u7b80\u5316\u524d\u9762\u7684\u9519\u8bef\u68c0\u67e5\u3002\u60a8\u73b0\u5728\u53ef\u4ee5\u7f16\u5199\u4ee5\u4e0b\u5185\u5bb9\uff0c\u800c\u4e0d\u662f\u663e\u5f0f\u6784\u5efa\u6761\u4ef6\u8bed\u53e5\u6765\u68c0\u67e5 null\uff1a<\/p>\n<p>static void TesterMethod(string[] args)<br \/>\n{<br \/>\n\/\/ We should check for null before accessing the array data! Console.WriteLine($&quot;You sent me {args?.Length} arguments.&quot;);<br \/>\n}<\/p>\n<p>In this case, you are not using a conditional statement. Rather, you are suffixing the ? operator directly after the string array variable. If the variable is null, its call to the Length property will not throw a runtime error. If you want to print an actual value, you could leverage the null-coalescing operator to assign a default value as so:<br \/>\n\u5728\u8fd9\u79cd\u60c5\u51b5\u4e0b\uff0c\u60a8\u6ca1\u6709\u4f7f\u7528\u6761\u4ef6\u8bed\u53e5\u3002\u76f8\u53cd\uff0c\u60a8\u6b63\u5728\u4e3a\uff1f \u8fd0\u7b97\u7b26\u76f4\u63a5\u5728\u5b57\u7b26\u4e32\u6570\u7ec4\u53d8\u91cf\u4e4b\u540e\u3002\u5982\u679c\u53d8\u91cf\u4e3a null\uff0c\u5219\u5b83\u5bf9 Length \u5c5e\u6027\u7684\u8c03\u7528\u4e0d\u4f1a\u5f15\u53d1\u8fd0\u884c\u65f6\u9519\u8bef\u3002\u5982\u679c\u8981\u6253\u5370\u5b9e\u9645\u503c\uff0c\u53ef\u4ee5\u5229\u7528 null \u5408\u5e76\u8fd0\u7b97\u7b26\u5206\u914d\u9ed8\u8ba4\u503c\uff0c\u5982\u4e0b\u6240\u793a\uff1a<\/p>\n<p>Console.WriteLine($&quot;You sent me {args?.Length ?? 0} arguments.&quot;);<\/p>\n<p>There are some additional areas of coding where the C# 6.0 null conditional operator will be quite handy, especially when working with delegates and events. Those topics are addressed later in the book (see Chapter 12), and you will see many more examples.<br \/>\n\u8fd8\u6709\u4e00\u4e9b\u5176\u4ed6\u7f16\u7801\u533a\u57df\uff0c\u5176\u4e2d C# 6.0 null \u6761\u4ef6\u8fd0\u7b97\u7b26\u5c06\u975e\u5e38\u65b9\u4fbf\uff0c\u5c24\u5176\u662f\u5728\u5904\u7406\u59d4\u6258\u548c\u4e8b\u4ef6\u65f6\u3002\u8fd9\u4e9b\u4e3b\u9898\u5c06\u5728\u672c\u4e66\u540e\u9762\u8ba8\u8bba\uff08\u89c1\u7b2c12\u7ae0\uff09\uff0c\u4f60\u5c06\u770b\u5230\u66f4\u591a\u7684\u4f8b\u5b50\u3002<\/p>\n<h2>Understanding Tuples (New\/Updated 7.0)<\/h2>\n<p>\u4e86\u89e3\u5143\u7ec4\uff08\u65b0\u589e\/\u66f4\u65b0 7.0\uff09<\/p>\n<p>To wrap up this chapter, let\u2019s examine the role of tuples using a Console Application project named FunWithTuples. As mentioned earlier in this chapter, one way to use out parameters is to retrieve more than one value from a method call. Another way is to use a light construct called a tuple.<br \/>\n\u4e3a\u4e86\u7ed3\u675f\u672c\u7ae0\uff0c\u8ba9\u6211\u4eec\u4f7f\u7528\u540d\u4e3a FunWithTuples \u7684\u63a7\u5236\u53f0\u5e94\u7528\u7a0b\u5e8f\u9879\u76ee\u6765\u68c0\u67e5\u5143\u7ec4\u7684\u4f5c\u7528\u3002\u5982\u672c\u7ae0\u524d\u9762\u6240\u8ff0\uff0c\u4f7f\u7528\u53c2\u6570\u7684\u4e00\u79cd\u65b9\u6cd5\u662f\u4ece\u65b9\u6cd5\u8c03\u7528\u4e2d\u68c0\u7d22\u591a\u4e2a\u503c\u3002\u53e6\u4e00\u79cd\u65b9\u6cd5\u662f\u4f7f\u7528\u79f0\u4e3a\u5143\u7ec4\u7684\u8f7b\u6784\u9020\u3002<\/p>\n<p>Tuples are lightweight data structures that contain multiple fields. They were added to the language in C# 6, but in an extremely limited way. There was also a potentially significant problem with the C# 6 implementation: each field is implemented as a reference type, potentially creating memory and\/or performance problems (from boxing\/unboxing).<br \/>\n\u5143\u7ec4\u662f\u5305\u542b\u591a\u4e2a\u5b57\u6bb5\u7684\u8f7b\u91cf\u7ea7\u6570\u636e\u7ed3\u6784\u3002\u5b83\u4eec\u88ab\u6dfb\u52a0\u5230 C# 6 \u7684\u8bed\u8a00\u4e2d\uff0c\u4f46\u65b9\u5f0f\u6781\u5176\u6709\u9650\u3002C \u8fd8\u6709\u4e00\u4e2a\u6f5c\u5728\u7684\u91cd\u5927\u95ee\u9898#6 \u5b9e\u73b0\uff1a\u6bcf\u4e2a\u5b57\u6bb5\u90fd\u4f5c\u4e3a\u5f15\u7528\u7c7b\u578b\u5b9e\u73b0\uff0c\u53ef\u80fd\u4f1a\u4ea7\u751f\u5185\u5b58\u548c\/\u6216\u6027\u80fd\u95ee\u9898\uff08\u6765\u81ea\u88c5\u7bb1\/\u53d6\u6d88\u88c5\u7bb1\uff09\u3002<\/p>\n<p>\u25a0 Note  Boxing occurs when a value type is stored as a reference variable (stored on the heap), and unboxing is when the value type is returned to a value type variable (stored on the stack). Boxing and unboxing and their performance implications are covered in depth in Chapter 10.<br \/>\n\u6ce8\u610f \u5f53\u503c\u7c7b\u578b\u5b58\u50a8\u4e3a\u5f15\u7528\u53d8\u91cf\uff08\u5b58\u50a8\u5728\u5806\u4e0a\uff09\u65f6\uff0c\u4f1a\u53d1\u751f\u88c5\u7bb1\uff0c\u800c\u53d6\u6d88\u88c5\u7bb1\u662f\u6307\u5c06\u503c\u7c7b\u578b\u8fd4\u56de\u5230\u503c\u7c7b\u578b\u53d8\u91cf\uff08\u5b58\u50a8\u5728\u5806\u6808\u4e0a\uff09\u3002\u7b2c10\u7ae0\u6df1\u5165\u4ecb\u7ecd\u4e86\u88c5\u7bb1\u548c\u62c6\u7bb1\u53ca\u5176\u6027\u80fd\u5f71\u54cd\u3002<\/p>\n<p>In C# 7, tuples use the new ValueTuple data type instead of reference types, potentially saving significant memory. The ValueTuple data type creates different structs based on the number of properties for a tuple. An additional feature added in C# 7 is that each property in a tuple can be assigned a specific name (just like variables), greatly enhancing the usability.<br \/>\n\u5728 C# 7 \u4e2d\uff0c\u5143\u7ec4\u4f7f\u7528\u65b0\u7684 ValueTuple \u6570\u636e\u7c7b\u578b\u800c\u4e0d\u662f\u5f15\u7528\u7c7b\u578b\uff0c\u8fd9\u53ef\u80fd\u4f1a\u8282\u7701\u5927\u91cf\u5185\u5b58\u3002ValueTuple \u6570\u636e\u7c7b\u578b\u6839\u636e\u5143\u7ec4\u7684\u5c5e\u6027\u6570\u521b\u5efa\u4e0d\u540c\u7684\u7ed3\u6784\u3002C# 7 \u4e2d\u6dfb\u52a0\u7684\u53e6\u4e00\u4e2a\u529f\u80fd\u662f\uff0c\u5143\u7ec4\u4e2d\u7684\u6bcf\u4e2a\u5c5e\u6027\u90fd\u53ef\u4ee5\u5206\u914d\u4e00\u4e2a\u7279\u5b9a\u540d\u79f0\uff08\u5c31\u50cf\u53d8\u91cf\u4e00\u6837\uff09\uff0c\u4ece\u800c\u5927\u5927\u63d0\u9ad8\u4e86\u53ef\u7528\u6027\u3002<\/p>\n<p>These are two important considerations for tuples:<br \/>\n\u4ee5\u4e0b\u662f\u5143\u7ec4\u7684\u4e24\u4e2a\u91cd\u8981\u6ce8\u610f\u4e8b\u9879\uff1a<\/p>\n<p>\u2022   The fields are not validated.<br \/>\n\u5b57\u6bb5\u672a\u9a8c\u8bc1\u3002<\/p>\n<p>\u2022   You cannot define your own methods.<br \/>\n\u2022 \u60a8\u65e0\u6cd5\u5b9a\u4e49\u81ea\u5df1\u7684\u65b9\u6cd5\u3002<\/p>\n<p>They are really designed to just be a lightweight data transport mechanism.<br \/>\n\u5b83\u4eec\u5b9e\u9645\u4e0a\u88ab\u8bbe\u8ba1\u4e3a\u4e00\u79cd\u8f7b\u91cf\u7ea7\u7684\u6570\u636e\u4f20\u8f93\u673a\u5236\u3002<\/p>\n<h2>Getting Started with Tuples<\/h2>\n<p>\u5143\u7ec4\u5165\u95e8<\/p>\n<p>Enough theory. Let\u2019s write some code! To create a tuple, simply enclose the values to be assigned to the tuple in parentheses, as follows:<br \/>\n\u8db3\u591f\u7684\u7406\u8bba\u3002\u8ba9\u6211\u4eec\u7f16\u5199\u4e00\u4e9b\u4ee3\u7801\uff01\u8981\u521b\u5efa\u5143\u7ec4\uff0c\u53ea\u9700\u5c06\u8981\u5206\u914d\u7ed9\u5143\u7ec4\u7684\u503c\u62ec\u5728\u62ec\u53f7\u4e2d\uff0c\u5982\u4e0b\u6240\u793a\uff1a<br \/>\n(&quot;a&quot;, 5, &quot;c&quot;)<\/p>\n<p>Notice that they do not all have to be the same data type. The parenthetical construct is also used to assign the tuple to a variable (or you can use the var keyword and the compiler will assign the data types for you). To assign the previous example to a variable, the following two lines achieve the same thing. The values variable will be a tuple with two string properties and an int property sandwiched in between.<br \/>\n\u8bf7\u6ce8\u610f\uff0c\u5b83\u4eec\u4e0d\u5fc5\u90fd\u662f\u76f8\u540c\u7684\u6570\u636e\u7c7b\u578b\u3002\u62ec\u53f7\u7ed3\u6784\u4e5f\u7528\u4e8e\u5c06\u5143\u7ec4\u5206\u914d\u7ed9\u53d8\u91cf\uff08\u6216\u8005\u60a8\u53ef\u4ee5\u4f7f\u7528 var \u5173\u952e\u5b57\uff0c\u7f16\u8bd1\u5668\u5c06\u4e3a\u60a8\u5206\u914d\u6570\u636e\u7c7b\u578b\uff09\u3002\u4e3a\u4e86\u5c06\u524d\u9762\u7684\u793a\u4f8b\u5206\u914d\u7ed9\u53d8\u91cf\uff0c\u4ee5\u4e0b\u4e24\u884c\u5b9e\u73b0\u76f8\u540c\u7684\u76ee\u6807\u3002values \u53d8\u91cf\u5c06\u662f\u4e00\u4e2a\u5143\u7ec4\uff0c\u5176\u4e2d\u5305\u542b\u4e24\u4e2a\u5b57\u7b26\u4e32\u5c5e\u6027\u548c\u4e00\u4e2a\u5939\u5728\u4e24\u8005\u4e4b\u95f4\u7684 int \u5c5e\u6027\u3002<\/p>\n<p>(string, int, string) values = (&quot;a&quot;, 5, &quot;c&quot;);<br \/>\nvar values = (&quot;a&quot;, 5, &quot;c&quot;);<\/p>\n<p>By default, the compiler assigns each property the name ItemX, where X represents the one-based position in the tuple. For the previous example, the property names are Item1, Item2, and Item3. Accessing them is done as follows:<br \/>\n\u9ed8\u8ba4\u60c5\u51b5\u4e0b\uff0c\u7f16\u8bd1\u5668\u4e3a\u6bcf\u4e2a\u5c5e\u6027\u5206\u914d\u540d\u79f0 ItemX\uff0c\u5176\u4e2d X \u8868\u793a\u5143\u7ec4\u4e2d\u4ece 1 \u5f00\u59cb\u7684\u4f4d\u7f6e\u3002\u5bf9\u4e8e\u524d\u9762\u7684\u793a\u4f8b\uff0c\u5c5e\u6027\u540d\u79f0\u4e3a Item2\u3001Item3 \u548c Item&lt;&gt;\u3002\u8bbf\u95ee\u5b83\u4eec\u7684\u65b9\u5f0f\u5982\u4e0b\uff1a<\/p>\n<p>Console.WriteLine($&quot;First item: {values.Item1}&quot;); Console.WriteLine($&quot;Second item: {values.Item2}&quot;); Console.WriteLine($&quot;Third item: {values.Item3}&quot;);<\/p>\n<p>Specific names can also be added to each property in the tuple on either the right side or the left side of the statement. While it is not a compiler error to assign names on both sides of the statement, if you do, the right side will be ignored, and only the left-side names are used. The following two lines of code show setting the names on the left and the right to achieve the same end:<br \/>\n\u8fd8\u53ef\u4ee5\u5c06\u7279\u5b9a\u540d\u79f0\u6dfb\u52a0\u5230\u8bed\u53e5\u53f3\u4fa7\u6216\u5de6\u4fa7\u5143\u7ec4\u4e2d\u7684\u6bcf\u4e2a\u5c5e\u6027\u3002\u867d\u7136\u5728\u8bed\u53e5\u7684\u4e24\u7aef\u5206\u914d\u540d\u79f0\u4e0d\u662f\u7f16\u8bd1\u5668\u9519\u8bef\uff0c\u4f46\u5982\u679c\u8fd9\u6837\u505a\uff0c\u53f3\u4fa7\u5c06\u88ab\u5ffd\u7565\uff0c\u4ec5\u4f7f\u7528\u5de6\u4fa7\u540d\u79f0\u3002\u4ee5\u4e0b\u4e24\u884c\u4ee3\u7801\u663e\u793a\u8bbe\u7f6e\u5de6\u4fa7\u548c\u53f3\u4fa7\u7684\u540d\u79f0\u4ee5\u5b9e\u73b0\u76f8\u540c\u7684\u76ee\u7684\uff1a<\/p>\n<p>(string FirstLetter, int TheNumber, string SecondLetter) valuesWithNames = (&quot;a&quot;, 5, &quot;c&quot;); var valuesWithNames2 = (FirstLetter: &quot;a&quot;, TheNumber: 5, SecondLetter: &quot;c&quot;);<\/p>\n<p>Now the properties on the tuple can be accessed using the field names as well as the ItemX notation, as shown in the following code:<br \/>\n\u73b0\u5728\uff0c\u53ef\u4ee5\u4f7f\u7528\u5b57\u6bb5\u540d\u79f0\u548c ItemX \u8868\u793a\u6cd5\u8bbf\u95ee\u5143\u7ec4\u4e0a\u7684\u5c5e\u6027\uff0c\u5982\u4ee5\u4e0b\u4ee3\u7801\u6240\u793a\uff1a<\/p>\n<p>Console.WriteLine($&quot;First item: {valuesWithNames.FirstLetter}&quot;); Console.WriteLine($&quot;Second item: {valuesWithNames.TheNumber}&quot;); Console.WriteLine($&quot;Third item: {valuesWithNames.SecondLetter}&quot;);<br \/>\n\/\/Using the item notation still works! Console.WriteLine($&quot;First item: {valuesWithNames.Item1}&quot;); Console.WriteLine($&quot;Second item: {valuesWithNames.Item2}&quot;); Console.WriteLine($&quot;Third item: {valuesWithNames.Item3}&quot;);<\/p>\n<p>Note that when setting the names on the right, you must use the keyword var to declare the variable. Setting the data types specifically (even without custom names) triggers the compiler to use the left side, assign the properties using the ItemX notation, and ignore any of the custom names set on the right. The following two examples ignore the Custom1 and Custom2 names:<br \/>\n\u8bf7\u6ce8\u610f\uff0c\u5728\u53f3\u4fa7\u8bbe\u7f6e\u540d\u79f0\u65f6\uff0c\u5fc5\u987b\u4f7f\u7528\u5173\u952e\u5b57 var \u6765\u58f0\u660e\u53d8\u91cf\u3002\u4e13\u95e8\u8bbe\u7f6e\u6570\u636e\u7c7b\u578b\uff08\u5373\u4f7f\u6ca1\u6709\u81ea\u5b9a\u4e49\u540d\u79f0\uff09\u4f1a\u89e6\u53d1\u7f16\u8bd1\u5668\u4f7f\u7528\u5de6\u4fa7\uff0c\u4f7f\u7528 ItemX \u8868\u793a\u6cd5\u5206\u914d\u5c5e\u6027\uff0c\u5e76\u5ffd\u7565\u53f3\u4fa7\u8bbe\u7f6e\u7684\u4efb\u4f55\u81ea\u5b9a\u4e49\u540d\u79f0\u3002\u4ee5\u4e0b\u4e24\u4e2a\u793a\u4f8b\u5ffd\u7565\u81ea\u5b9a\u4e49 1 \u548c\u81ea\u5b9a\u4e49 2 \u540d\u79f0\uff1a<\/p>\n<p>(int, int) example = (Custom1:5, Custom2:7);<br \/>\n(int Field1, int Field2) example = (Custom1:5, Custom2:7);<\/p>\n<p>It is also important to call out that the custom field names exist only at compile time and are not available when inspecting the tuple at runtime using reflection (reflection is covered in Chapter 17).<br \/>\n\u540c\u6837\u91cd\u8981\u7684\u662f\u8981\u6307\u51fa\uff0c\u81ea\u5b9a\u4e49\u5b57\u6bb5\u540d\u79f0\u4ec5\u5728\u7f16\u8bd1\u65f6\u5b58\u5728\uff0c\u5e76\u4e14\u5728\u8fd0\u884c\u65f6\u4f7f\u7528\u53cd\u5c04\u68c0\u67e5\u5143\u7ec4\u65f6\u4e0d\u53ef\u7528\uff08\u53cd\u5c04\u5728\u7b2c 17 \u7ae0\u4e2d\u4ecb\u7ecd\uff09\u3002<\/p>\n<p>Tuples can also be nested as tuples inside of tuples. Since each property in a tuple is a data type and a tuple is a data type, the following code is perfectly legitimate:<br \/>\n\u5143\u7ec4\u4e5f\u53ef\u4ee5\u4f5c\u4e3a\u5143\u7ec4\u5d4c\u5957\u5728\u5143\u7ec4\u5185\u3002\u7531\u4e8e\u5143\u7ec4\u4e2d\u7684\u6bcf\u4e2a\u5c5e\u6027\u90fd\u662f\u6570\u636e\u7c7b\u578b\uff0c\u5143\u7ec4\u662f\u6570\u636e\u7c7b\u578b\uff0c\u56e0\u6b64\u4ee5\u4e0b\u4ee3\u7801\u662f\u5b8c\u5168\u5408\u6cd5\u7684\uff1a<\/p>\n<p>Console.WriteLine(&quot;=&gt; Nested Tuples&quot;); var nt = (5, 4, (&quot;a&quot;, &quot;b&quot;));<\/p>\n<h2>Using Inferred Variable Names (Updated 7.1)<\/h2>\n<p>\u4f7f\u7528\u63a8\u65ad\u7684\u53d8\u91cf\u540d\u79f0\uff087.1 \u66f4\u65b0\uff09<\/p>\n<p>An update to tuples in C# 7.1 is the ability for C# to infer the variable names of tuples, as shown here:<br \/>\nC# 7.1 \u4e2d\u5bf9\u5143\u7ec4\u7684\u66f4\u65b0\u662f C# \u80fd\u591f\u63a8\u65ad\u5143\u7ec4\u7684\u53d8\u91cf\u540d\u79f0\uff0c\u5982\u4e0b\u6240\u793a\uff1a<\/p>\n<p>Console.WriteLine(&quot;=&gt; Inferred Tuple Names&quot;);<br \/>\nvar foo = new {Prop1 = &quot;first&quot;, Prop2 = &quot;second&quot;}; var bar = (foo.Prop1, foo.Prop2); Console.WriteLine($&quot;{bar.Prop1};{bar.Prop2}&quot;);<\/p>\n<h2>Understanding Tuple Equality\/Inequality (New 7.3)<\/h2>\n<p>\u4e86\u89e3\u5143\u7ec4\u76f8\u7b49\/\u4e0d\u7b49\u5f0f\uff08\u65b0 7.3\uff09<\/p>\n<p>An added feature in C# 7.3 is the tuple equality (==) and inequality (!=). When testing for inequality, the comparison operators will perform implicit conversions on data types within the tuples, including comparing nullable and non-nullable tuples and\/or properties. That means the following tests work perfectly, despite the difference between int\/long:<br \/>\nC# 7.3 \u4e2d\u65b0\u589e\u7684\u529f\u80fd\u662f\u5143\u7ec4\u76f8\u7b49 \uff08==\uff09 \u548c\u4e0d\u76f8\u7b49 \uff08\uff01=\uff09\u3002\u6d4b\u8bd5\u4e0d\u7b49\u5f0f\u65f6\uff0c\u6bd4\u8f83\u8fd0\u7b97\u7b26\u5c06\u5bf9\u5143\u7ec4\u4e2d\u7684\u6570\u636e\u7c7b\u578b\u6267\u884c\u9690\u5f0f\u8f6c\u6362\uff0c\u5305\u62ec\u6bd4\u8f83\u53ef\u4e3a\u7a7a\u548c\u4e0d\u53ef\u4e3a\u7a7a\u7684\u5143\u7ec4\u548c\/\u6216\u5c5e\u6027\u3002\u8fd9\u610f\u5473\u7740\u4ee5\u4e0b\u6d4b\u8bd5\u53ef\u4ee5\u5b8c\u7f8e\u8fd0\u884c\uff0c\u5c3d\u7ba1 int\/long \u4e4b\u95f4\u5b58\u5728\u5dee\u5f02\uff1a<\/p>\n<p>Console.WriteLine(&quot;=&gt; Tuples Equality\/Inequality&quot;);<br \/>\n\/\/ lifted conversions<br \/>\nvar left = (a: 5, b: 10);<br \/>\n(int? a, int? b) nullableMembers = (5, 10); Console.WriteLine(left == nullableMembers); \/\/ Also true<br \/>\n\/\/ converted type of left is (long, long) (long a, long b) longTuple = (5, 10);<br \/>\nConsole.WriteLine(left == longTuple); \/\/ Also true<br \/>\n\/\/ comparisons performed on (long, long) tuples (long a, int b) longFirst = (5, 10);<br \/>\n(int a, long b) longSecond = (5, 10); Console.WriteLine(longFirst == longSecond); \/\/ Also true<\/p>\n<p>Tuples that contain tuples can also be compared, but only if they have the same shape. You cannot compare one tuple of three int properties with another tuple of two ints and a tuple.<br \/>\n\u4e5f\u53ef\u4ee5\u6bd4\u8f83\u5305\u542b\u5143\u7ec4\u7684\u5143\u7ec4\uff0c\u4f46\u524d\u63d0\u662f\u5b83\u4eec\u7684\u5f62\u72b6\u76f8\u540c\u3002\u4e0d\u80fd\u5c06\u4e00\u4e2a\u5305\u542b\u4e09\u4e2a int \u5c5e\u6027\u7684\u5143\u7ec4\u4e0e\u53e6\u4e00\u4e2a\u5305\u542b\u4e24\u4e2a int\u548c\u4e00\u4e2a\u5143\u7ec4\u7684\u5143\u7ec4\u8fdb\u884c\u6bd4\u8f83\u3002<\/p>\n<h2>Understanding Tuples as Method Return Values<\/h2>\n<p>\u5c06\u5143\u7ec4\u7406\u89e3\u4e3a\u65b9\u6cd5\u8fd4\u56de\u503c<\/p>\n<p>Earlier in this chapter, out parameters were used to return more than one value from a method call. There are additional ways to do this, such as creating a class or structure specifically to return the values. But if this class or struct is to be used as a data transport for only one method, that is extra work and extra code that does not need to be developed. Tuples are perfectly suited for this task, are lightweight, and are easy to declare and use.<br \/>\n\u5728\u672c\u7ae0\u524d\u9762\uff0cout \u53c2\u6570\u7528\u4e8e\u4ece\u65b9\u6cd5\u8c03\u7528\u8fd4\u56de\u591a\u4e2a\u503c\u3002\u8fd8\u6709\u5176\u4ed6\u65b9\u6cd5\u53ef\u4ee5\u6267\u884c\u6b64\u64cd\u4f5c\uff0c\u4f8b\u5982\u521b\u5efa\u4e13\u95e8\u7528\u4e8e\u8fd4\u56de\u503c\u7684\u7c7b\u6216\u7ed3\u6784\u3002\u4f46\u662f\uff0c\u5982\u679c\u4ec5\u5c06\u6b64\u7c7b\u6216\u7ed3\u6784\u7528\u4f5c\u4e00\u79cd\u65b9\u6cd5\u7684\u6570\u636e\u4f20\u8f93\uff0c\u90a3\u5c31\u662f\u4e0d\u9700\u8981\u5f00\u53d1\u7684\u989d\u5916\u5de5\u4f5c\u548c\u989d\u5916\u4ee3\u7801\u3002\u5143\u7ec4\u975e\u5e38\u9002\u5408\u6b64\u4efb\u52a1\uff0c\u662f\u8f7b\u91cf\u7ea7\u7684\uff0c\u5e76\u4e14\u6613\u4e8e\u58f0\u660e\u548c\u4f7f\u7528\u3002<\/p>\n<p>This is one of the examples from the out parameter section. It returns three values but requires three parameters passed in as transport mechanisms for the calling code.<br \/>\n\u8fd9\u662f out \u53c2\u6570\u90e8\u5206\u4e2d\u7684\u793a\u4f8b\u4e4b\u4e00\u3002\u5b83\u8fd4\u56de\u4e09\u4e2a\u503c\uff0c\u4f46\u9700\u8981\u4e09\u4e2a\u53c2\u6570\u4f5c\u4e3a\u8c03\u7528\u4ee3\u7801\u7684\u4f20\u8f93\u673a\u5236\u4f20\u5165\u3002<\/p>\n<p>static void FillTheseValues(out int a, out string b, out bool c)<br \/>\n{<br \/>\na = 9;<br \/>\nb = &quot;Enjoy your string.&quot;; c = true;<br \/>\n}<\/p>\n<p>By using a tuple, you can remove the parameters and still get the three values back.<br \/>\n\u901a\u8fc7\u4f7f\u7528\u5143\u7ec4\uff0c\u53ef\u4ee5\u5220\u9664\u53c2\u6570\u5e76\u4ecd\u8fd4\u56de\u4e09\u4e2a\u503c\u3002<\/p>\n<p>static (int a,string b,bool c) FillTheseValues()<br \/>\n{<br \/>\nreturn (9,&quot;Enjoy your string.&quot;,true);<br \/>\n}<\/p>\n<p>Calling this method is as simple as calling any other method.<br \/>\n\u8c03\u7528\u6b64\u65b9\u6cd5\u4e0e\u8c03\u7528\u4efb\u4f55\u5176\u4ed6\u65b9\u6cd5\u4e00\u6837\u7b80\u5355\u3002<\/p>\n<p>var samples = FillTheseValues(); Console.WriteLine($&quot;Int is: {samples.a}&quot;);<\/p>\n<p>Console.WriteLine($&quot;String is: {samples.b}&quot;); Console.WriteLine($&quot;Boolean is: {samples.c}&quot;);<\/p>\n<p>Perhaps a better example is deconstructing a full name into its individual parts (first, middle, last). The following code takes in a full name and returns a tuple with the different parts:<br \/>\n\u4e5f\u8bb8\u4e00\u4e2a\u66f4\u597d\u7684\u4f8b\u5b50\u662f\u5c06\u5168\u540d\u89e3\u6784\u4e3a\u5176\u5404\u4e2a\u90e8\u5206\uff08\u7b2c\u4e00\u4e2a\u3001\u4e2d\u95f4\u3001\u6700\u540e\u4e00\u4e2a\uff09\u3002\u4ee5\u4e0b\u4ee3\u7801\u91c7\u7528\u5168\u540d\u5e76\u8fd4\u56de\u5305\u542b\u4e0d\u540c\u90e8\u5206\u7684\u5143\u7ec4\uff1a<\/p>\n<p>static (string first, string middle, string last) SplitNames(string fullName)<br \/>\n{<br \/>\n\/\/do what is needed to split the name apart return (&quot;Philip&quot;, &quot;F&quot;, &quot;Japikse&quot;);<br \/>\n}<\/p>\n<h2>Understanding Discards with Tuples<\/h2>\n<p>\u4e86\u89e3\u4f7f\u7528\u5143\u7ec4\u7684\u4e22\u5f03<br \/>\nFollowing up on the SplitNames() example, suppose you know that you need only the first and last names and do not care about the middle. By providing variable names for the values you want returned and filling in the unneeded values with an underscore (<em>) placeholder, you can refine the return value like this:<br \/>\n\u7ee7\u7eed SplitNames\uff08\uff09 \u793a\u4f8b\uff0c\u5047\u8bbe\u60a8\u77e5\u9053\u60a8\u53ea\u9700\u8981\u540d\u5b57\u548c\u59d3\u6c0f\uff0c\u800c\u4e0d\u5173\u5fc3\u4e2d\u95f4\u3002\u901a\u8fc7\u4e3a\u8981\u8fd4\u56de\u7684\u503c\u63d0\u4f9b\u53d8\u91cf\u540d\u79f0\uff0c\u5e76\u4f7f\u7528\u4e0b\u5212\u7ebf \uff08<\/em>\uff09 \u5360\u4f4d\u7b26\u586b\u5145\u4e0d\u9700\u8981\u7684\u503c\uff0c\u53ef\u4ee5\u50cf\u8fd9\u6837\u4f18\u5316\u8fd4\u56de\u503c\uff1a<\/p>\n<p>var (first, _, last) = SplitNames(&quot;Philip F Japikse&quot;); Console.WriteLine($&quot;{first}:{last}&quot;);<\/p>\n<p>The middle name value of the tuple is discarded.<br \/>\n\u5143\u7ec4\u7684\u4e2d\u95f4\u540d\u503c\u5c06\u88ab\u4e22\u5f03\u3002<\/p>\n<h2>Understanding Tuple Pattern Matching switch Expressions (New 8.0)<\/h2>\n<p>\u4e86\u89e3\u5143\u7ec4\u6a21\u5f0f\u5339\u914d\u5f00\u5173\u8868\u8fbe\u5f0f\uff08\u65b0 8.0\uff09<\/p>\n<p>Now that you have a thorough understanding of tuples, it is time to revisit the switch expression with tuples from Chapter 3. Here is the example again:<br \/>\n\u73b0\u5728\u60a8\u5df2\u7ecf\u5bf9\u5143\u7ec4\u6709\u4e86\u900f\u5f7b\u7684\u4e86\u89e3\uff0c\u662f\u65f6\u5019\u91cd\u65b0\u5ba1\u89c6\u7b2c 3 \u7ae0\u4e2d\u5e26\u6709\u5143\u7ec4\u7684 switch \u8868\u8fbe\u5f0f\u4e86\u3002\u8fd9\u91cc\u53c8\u662f\u4e00\u4e2a\u4f8b\u5b50\uff1a<\/p>\n<p>\/\/Switch expression with Tuples<br \/>\nstatic string RockPaperScissors(string first, string second)<br \/>\n{<br \/>\nreturn (first, second) switch<br \/>\n{<br \/>\n(&quot;rock&quot;, &quot;paper&quot;) =&gt; &quot;Paper wins.&quot;,<br \/>\n(&quot;rock&quot;, &quot;scissors&quot;) =&gt; &quot;Rock wins.&quot;,<br \/>\n(&quot;paper&quot;, &quot;rock&quot;) =&gt; &quot;Paper wins.&quot;, (&quot;paper&quot;, &quot;scissors&quot;) =&gt; &quot;Scissors wins.&quot;, (&quot;scissors&quot;, &quot;rock&quot;) =&gt; &quot;Rock wins.&quot;, (&quot;scissors&quot;, &quot;paper&quot;) =&gt; &quot;Scissors wins.&quot;, (<em>, <\/em>) =&gt; &quot;Tie.&quot;,<br \/>\n};<br \/>\n}<\/p>\n<p>In this example, the two parameters are converted into a tuple as they are passed into the switch expression. The relevant values are represented in the switch expression, and any other cases are handled by the final tuple, which is composed of two discards.<br \/>\nThe RockPaperScissors() method signature could also be written to take in a tuple, like this:<br \/>\n\u5728\u6b64\u793a\u4f8b\u4e2d\uff0c\u8fd9\u4e24\u4e2a\u53c2\u6570\u5728\u4f20\u9012\u5230 switch \u8868\u8fbe\u5f0f\u65f6\u5c06\u8f6c\u6362\u4e3a\u5143\u7ec4\u3002\u76f8\u5173\u503c\u5728 switch \u8868\u8fbe\u5f0f\u4e2d\u8868\u793a\uff0c\u4efb\u4f55\u5176\u4ed6\u60c5\u51b5\u7531\u6700\u7ec8\u5143\u7ec4\u5904\u7406\uff0c\u8be5\u5143\u7ec4\u7531\u4e24\u4e2a\u4e22\u5f03\u7ec4\u6210\u3002RockPaperScissors\uff08\uff09 \u65b9\u6cd5\u7b7e\u540d\u4e5f\u53ef\u4ee5\u5199\u6210\u63a5\u53d7\u5143\u7ec4\uff0c\u5982\u4e0b\u6240\u793a\uff1a<\/p>\n<p>static string RockPaperScissors( (string first, string second) value)<\/p>\n<p>{<br \/>\nreturn value switch<br \/>\n{<br \/>\n\/\/omitted for brevity<br \/>\n};<br \/>\n}<\/p>\n<h2>Deconstructing Tuples (Updated 10.0)<\/h2>\n<p>\u89e3\u6784\u5143\u7ec4\uff0810.0 \u66f4\u65b0\uff09<\/p>\n<p>Deconstructing is the term given when separating out the properties of a tuple to be used individually. The SplitNames() example did just that. The first and last variables were accessed independently from any tuple construct. The variables can be initialized while deconstructing the tuple, or they can be pre-initialized. The following examples show both patterns:<br \/>\n\u89e3\u6784\u662f\u5728\u5206\u79bb\u8981\u5355\u72ec\u4f7f\u7528\u7684\u5143\u7ec4\u7684\u5c5e\u6027\u65f6\u7ed9\u51fa\u7684\u672f\u8bed\u3002SplitNames\uff08\uff09 \u793a\u4f8b\u5c31\u662f\u8fd9\u6837\u505a\u7684\u3002\u7b2c\u4e00\u4e2a\u548c\u6700\u540e\u4e00\u4e2a\u53d8\u91cf\u72ec\u7acb\u4e8e\u4efb\u4f55\u5143\u7ec4\u6784\u9020\u8fdb\u884c\u8bbf\u95ee\u3002\u53d8\u91cf\u53ef\u4ee5\u5728\u89e3\u6784\u5143\u7ec4\u65f6\u521d\u59cb\u5316\uff0c\u4e5f\u53ef\u4ee5\u9884\u5148\u521d\u59cb\u5316\u3002\u4ee5\u4e0b\u793a\u4f8b\u663e\u793a\u4e86\u8fd9\u4e24\u79cd\u6a21\u5f0f\uff1a<\/p>\n<p>(int X, int Y) myTuple = (4,5); int x = 0;<br \/>\nint y = 0;<br \/>\n(x,y) = myTuple; Console.WriteLine($&quot;X is: {x}&quot;); Console.WriteLine($&quot;Y is: {y}&quot;); (int x1, int y1) = myTuple; Console.WriteLine($&quot;x1 is: {x}&quot;); Console.WriteLine($&quot;y1 is: {y}&quot;);<\/p>\n<p>New in C# 10, the assignment and declaration can be mixed, as the following shows:<br \/>\nC# 10 \u4e2d\u7684\u65b0\u589e\u529f\u80fd\u662f\uff0c\u8d4b\u503c\u548c\u58f0\u660e\u53ef\u4ee5\u6df7\u5408\u4f7f\u7528\uff0c\u5982\u4e0b\u6240\u793a\uff1a<\/p>\n<p>int x2 = 0;<br \/>\n(x2, int y2) = myTuple; Console.WriteLine($&quot;x2 is: {x}&quot;); Console.WriteLine($&quot;y2 is: {y}&quot;);<\/p>\n<p>There is another use for this pattern that can be helpful, and that is deconstructing custom types. Take a shorter version of the Point structure used earlier in this chapter. A new method named Deconstruct() has been added to return the individual properties of the Point instance as a tuple with properties named XPos and YPos.<br \/>\n\u6b64\u6a21\u5f0f\u8fd8\u6709\u53e6\u4e00\u4e2a\u6709\u7528\u7684\u7528\u9014\uff0c\u90a3\u5c31\u662f\u89e3\u6784\u81ea\u5b9a\u4e49\u7c7b\u578b\u3002\u4ee5\u672c\u7ae0\u524d\u9762\u4f7f\u7528\u7684 Point \u7ed3\u6784\u7684\u8f83\u77ed\u7248\u672c\u4e3a\u4f8b\u3002\u6dfb\u52a0\u4e86\u4e00\u4e2a\u540d\u4e3a Deconstruct\uff08\uff09 \u7684\u65b0\u65b9\u6cd5\uff0c\u7528\u4e8e\u5c06 Point \u5b9e\u4f8b\u7684\u5404\u4e2a\u5c5e\u6027\u4f5c\u4e3a\u5143\u7ec4\u8fd4\u56de\uff0c\u5176\u4e2d\u5305\u542b\u540d\u4e3a XPos \u548c YPos \u7684\u5c5e\u6027\u3002<\/p>\n<p>struct Point<br \/>\n{<br \/>\n\/\/ Fields of the structure. public int X;<br \/>\npublic int Y;<\/p>\n<p>\/\/ A custom constructor.<br \/>\npublic Point(int XPos, int YPos)<br \/>\n{<br \/>\nX = XPos;<br \/>\nY = YPos;<br \/>\n}<\/p>\n<p>public (int XPos, int YPos) Deconstruct() =&gt; (X, Y);<br \/>\n}<\/p>\n<p>Notice the new Deconstruct() method, shown in bold in the previous code listing. This method can be named anything, but by convention it is typically named Deconstruct(). This allows a single method call to get the individual values of the structure by returning a tuple.<br \/>\n\u8bf7\u6ce8\u610f\u65b0\u7684 Deconstruct\uff08\uff09 \u65b9\u6cd5\uff0c\u5728\u524d\u9762\u7684\u4ee3\u7801\u6e05\u5355\u4e2d\u4ee5\u7c97\u4f53\u663e\u793a\u3002\u6b64\u65b9\u6cd5\u53ef\u4ee5\u547d\u540d\u4e3a\u4efb\u4f55\u540d\u79f0\uff0c\u4f46\u6309\u7167\u60ef\u4f8b\uff0c\u5b83\u901a\u5e38\u88ab\u547d\u540d\u4e3a Deconstruct\uff08\uff09\u3002\u8fd9\u5141\u8bb8\u5355\u4e2a\u65b9\u6cd5\u8c03\u7528\u901a\u8fc7\u8fd4\u56de\u5143\u7ec4\u6765\u83b7\u53d6\u7ed3\u6784\u7684\u5404\u4e2a\u503c\u3002<\/p>\n<p>Point p = new Point(7,5);<br \/>\nvar pointValues = p.Deconstruct(); Console.WriteLine($&quot;X is: {pointValues.XPos}&quot;); Console.WriteLine($&quot;Y is: {pointValues.YPos}&quot;);<\/p>\n<h2>Deconstructing Tuples with Positional Pattern Matching (New 8.0)<\/h2>\n<p>\u4f7f\u7528\u4f4d\u7f6e\u6a21\u5f0f\u5339\u914d\u89e3\u6784\u5143\u7ec4\uff08\u65b0 8.0\uff09<\/p>\n<p>When tuples have an accessible Deconstruct() method, the deconstruction can happen implicitly without having to call the Deconstruct() method. The following code shows this implicit deconstruction:<br \/>\n\u5f53\u5143\u7ec4\u5177\u6709\u53ef\u8bbf\u95ee\u7684 Deconstruct\uff08\uff09 \u65b9\u6cd5\u65f6\uff0c\u89e3\u6784\u53ef\u4ee5\u9690\u5f0f\u53d1\u751f\uff0c\u800c\u65e0\u9700\u8c03\u7528 Deconstruct\uff08\uff09 \u65b9\u6cd5\u3002\u4ee5\u4e0b\u4ee3\u7801\u663e\u793a\u4e86\u8fd9\u79cd\u9690\u5f0f\u89e3\u6784\uff1a<\/p>\n<p>Point p2 = new Point(8,3); int xp2 = 0;<br \/>\nint yp2 = 0; (xp2,yp2) = p2;<br \/>\nConsole.WriteLine($&quot;XP2 is: {xp2}&quot;); Console.WriteLine($&quot;YP2 is: {yp2}&quot;);<\/p>\n<p>Additionally, the deconstruction can be used in a tuple-based switch expression. Using the Point example, the following code uses the generated tuple and uses those values for the when clause of each expression:<br \/>\n\u6b64\u5916\uff0c\u89e3\u6784\u53ef\u7528\u4e8e\u57fa\u4e8e\u5143\u7ec4\u7684\u5f00\u5173\u8868\u8fbe\u5f0f\u3002\u4f7f\u7528 Point \u793a\u4f8b\uff0c\u4ee5\u4e0b\u4ee3\u7801\u4f7f\u7528\u751f\u6210\u7684\u5143\u7ec4\uff0c\u5e76\u5c06\u8fd9\u4e9b\u503c\u7528\u4e8e\u6bcf\u4e2a\u8868\u8fbe\u5f0f\u7684 when \u5b50\u53e5\uff1a<\/p>\n<p>static string GetQuadrant1(Point p)<br \/>\n{<br \/>\nreturn p.Deconstruct() switch<br \/>\n{<br \/>\n(0, 0) =&gt; &quot;Origin&quot;,<br \/>\nvar (x, y) when x &gt; 0 &amp;&amp; y &gt; 0 =&gt; &quot;One&quot;, var (x, y) when x &lt; 0 &amp;&amp; y &gt; 0 =&gt; &quot;Two&quot;, var (x, y) when x &lt; 0 &amp;&amp; y &lt; 0 =&gt; &quot;Three&quot;, var (x, y) when x &gt; 0 &amp;&amp; y &lt; 0 =&gt; &quot;Four&quot;, var (<em>, <\/em>) =&gt; &quot;Border&quot;,<br \/>\n};<br \/>\n}<\/p>\n<p>If the Deconstruct() method is defined with two out parameters, then the switch expression will automatically deconstruct the point. Add another Deconstruct method to the Point as follows:<br \/>\n\u5982\u679c Deconstruct\uff08\uff09 \u65b9\u6cd5\u4f7f\u7528\u4e24\u4e2a out \u53c2\u6570\u5b9a\u4e49\uff0c\u5219 switch \u8868\u8fbe\u5f0f\u5c06\u81ea\u52a8\u89e3\u6784\u8be5\u70b9\u3002\u5c06\u53e6\u4e00\u4e2a\u89e3\u6784\u65b9\u6cd5\u6dfb\u52a0\u5230 Point\uff0c\u5982\u4e0b\u6240\u793a\uff1a<\/p>\n<p>public void Deconstruct(out int XPos, out int YPos)<br \/>\n=&gt; (XPos,YPos)=(X, Y);<\/p>\n<p>Now you can update (or add a new) GetQuadrant() method to this:<\/p>\n<p>static string GetQuadrant2(Point p)<br \/>\n{<br \/>\nreturn p switch<br \/>\n{<br \/>\n(0, 0) =&gt; &quot;Origin&quot;,<\/p>\n<p>var (x, y) when x &gt; 0 &amp;&amp; y &gt; 0 =&gt; &quot;One&quot;, var (x, y) when x &lt; 0 &amp;&amp; y &gt; 0 =&gt; &quot;Two&quot;, var (x, y) when x &lt; 0 &amp;&amp; y &lt; 0 =&gt; &quot;Three&quot;, var (x, y) when x &gt; 0 &amp;&amp; y &lt; 0 =&gt; &quot;Four&quot;, var (<em>, <\/em>) =&gt; &quot;Border&quot;,<br \/>\n};<br \/>\n}<\/p>\n<p>The change is very subtle (and is highlighted in bold). Instead of calling p.Deconstruct(), just the Point variable is used in the switch expression.<br \/>\n\u66f4\u6539\u975e\u5e38\u5fae\u5999\uff08\u5e76\u4ee5\u7c97\u4f53\u7a81\u51fa\u663e\u793a\uff09\u3002\u800c\u4e0d\u662f\u8c03\u7528 p.Deconstruct\uff08\uff09\uff0c\u53ea\u9700\u70b9\u53d8\u91cf\u7528\u4e8e\u5f00\u5173\u8868\u8fbe\u5f0f\u3002<\/p>\n<p>Summary<br \/>\n\u603b\u7ed3<br \/>\nThis chapter began with an examination of arrays. Then, we discussed the C# keywords that allow you to build custom methods. Recall that by default parameters are passed by value; however, you may pass a parameter by reference if you mark it with ref or out. You also learned about the role of optional or named parameters and how to define and invoke methods taking parameter arrays.<br \/>\n\u672c\u7ae0\u4ece\u5bf9\u6570\u7ec4\u7684\u68c0\u67e5\u5f00\u59cb\u3002\u7136\u540e\uff0c\u6211\u4eec\u8ba8\u8bba\u4e86\u5141\u8bb8\u60a8\u751f\u6210\u81ea\u5b9a\u4e49\u65b9\u6cd5\u7684 C# \u5173\u952e\u5b57\u3002\u56de\u60f3\u4e00\u4e0b\uff0c\u9ed8\u8ba4\u60c5\u51b5\u4e0b\u53c2\u6570\u662f\u6309\u503c\u4f20\u9012\u7684;\u4f46\u662f\uff0c\u5982\u679c\u4f7f\u7528 ref \u6216 out \u6807\u8bb0\u53c2\u6570\uff0c\u5219\u53ef\u4ee5\u901a\u8fc7\u5f15\u7528\u4f20\u9012\u53c2\u6570\u3002\u60a8\u8fd8\u4e86\u89e3\u4e86\u53ef\u9009\u6216\u547d\u540d\u53c2\u6570\u7684\u89d2\u8272\uff0c\u4ee5\u53ca\u5982\u4f55\u5b9a\u4e49\u548c\u8c03\u7528\u91c7\u7528\u53c2\u6570\u6570\u7ec4\u7684\u65b9\u6cd5\u3002<\/p>\n<p>After you investigated the topic of method overloading, the bulk of this chapter examined several details regarding how enumerations and structures are defined in C# and represented within the .NET Core base class libraries. Along the way, you examined several details regarding value types and reference types, including how they respond when passing them as parameters to methods and how to interact with nullable data types and variables that might be null (e.g., reference type variables and nullable value type variables) using the ?, ??, and ??= operators.<br \/>\n\u5728\u7814\u7a76\u4e86\u65b9\u6cd5\u91cd\u8f7d\u7684\u4e3b\u9898\u4e4b\u540e\uff0c\u672c\u7ae0\u7684\u5927\u90e8\u5206\u5185\u5bb9\u68c0\u67e5\u4e86\u6709\u5173\u5982\u4f55\u5728 C# \u4e2d\u5b9a\u4e49\u679a\u4e3e\u548c\u7ed3\u6784\u4ee5\u53ca\u5982\u4f55\u5728 .NET Core \u57fa\u7c7b\u5e93\u4e2d\u8868\u793a\u679a\u4e3e\u548c\u7ed3\u6784\u7684\u51e0\u4e2a\u8be6\u7ec6\u4fe1\u606f\u3002\u5728\u6b64\u8fc7\u7a0b\u4e2d\uff0c\u60a8\u68c0\u67e5\u4e86\u6709\u5173\u503c\u7c7b\u578b\u548c\u5f15\u7528\u7c7b\u578b\u7684\u51e0\u4e2a\u8be6\u7ec6\u4fe1\u606f\uff0c\u5305\u62ec\u5b83\u4eec\u4f5c\u4e3a\u53c2\u6570\u4f20\u9012\u7ed9\u65b9\u6cd5\u65f6\u7684\u54cd\u5e94\u65b9\u5f0f\uff0c\u4ee5\u53ca\u5982\u4f55\u4f7f\u7528 \uff1f\u3001\uff1f\uff1f\u548c \uff1f\uff1f= \u8fd0\u7b97\u7b26\u3002<\/p>\n<p>The final section of the chapter investigated a long-anticipated feature in C#, tuples. After getting an understanding of what they are and how they work, you used them to return multiple values from methods as well as to deconstruct custom types.<br \/>\n\u672c\u7ae0\u7684\u6700\u540e\u4e00\u90e8\u5206\u63a2\u8ba8\u4e86 C# \u4e2d\u671f\u5f85\u5df2\u4e45\u7684\u529f\u80fd\uff0c\u5373\u5143\u7ec4\u3002\u5728\u4e86\u89e3\u5b83\u4eec\u662f\u4ec0\u4e48\u4ee5\u53ca\u5b83\u4eec\u5982\u4f55\u5de5\u4f5c\u4e4b\u540e\uff0c\u60a8\u4f7f\u7528\u5b83\u4eec\u4ece\u65b9\u6cd5\u8fd4\u56de\u591a\u4e2a\u503c\u4ee5\u53ca\u89e3\u6784\u81ea\u5b9a\u4e49\u7c7b\u578b\u3002<\/p>\n<p>In Chapter 5, you will begin to dig into the details of object-oriented development.<br \/>\n\u5728\u7b2c5\u7ae0\u4e2d\uff0c\u60a8\u5c06\u5f00\u59cb\u6df1\u5165\u7814\u7a76\u9762\u5411\u5bf9\u8c61\u5f00\u53d1\u7684\u7ec6\u8282\u3002<\/p>\n","protected":false},"excerpt":{"rendered":"<p>CHAPTER 4 \u7b2c4\u7ae0 Core C# Programming Constructs, Part 2 \u6838\u5fc3 C# \u7f16\u7a0b \u6784\u9020\uff0c\u7b2c 2 \u90e8\u5206 This chapter picks up where Chapter 3 left off and completes your investigation of the core aspects of the C# programming language. You will start with an investigation of the details behind manipulating arrays using the syntax of C# and [&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-292","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\/292","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=292"}],"version-history":[{"count":0,"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/posts\/292\/revisions"}],"wp:attachment":[{"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=292"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=292"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=292"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}