C#4.0新增功能02 命名實參和可選實參
- 2019 年 10 月 4 日
- 筆記
C# 4 介紹命名實參和可選實參。 通過命名實參,你可以為特定形參指定實參,方法是將實參與該形參的名稱關聯,而不是與形參在形參列表中的位置關聯。 通過可選參數,你可以為某些形參省略實參。 這兩種技術都可與方法、索引器、構造函數和委託一起使用。
使用命名參數和可選參數時,將按實參出現在實參列表(而不是形參列表)中的順序計算這些實參。
命名形參和可選形參一起使用時,你可以只為可選形參列表中的少數形參提供實參。 此功能極大地方便了對 COM 介面(例如 Microsoft Office 自動化 API)的調用。
命名實參
有了命名實參,你將不再需要記住或查找形參在所調用方法的形參列表中的順序。 每個實參的形參都可按形參名稱進行指定。 例如,通過以函數定義的順序按位置發送實參,可以採用標準方式調用列印訂單詳細資訊(例如賣家姓名、訂單號和產品名稱)的函數。
PrintOrderDetails("Gift Shop", 31, "Red Mug");
如果不記得形參的順序,但卻知道其名稱,則可以按任意順序發送實參。
PrintOrderDetails(orderNum: 31, productName: "Red Mug", sellerName: "Gift Shop");
PrintOrderDetails(productName: "Red Mug", sellerName: "Gift Shop", orderNum: 31);
命名實參還可以標識每個實參所表示的含義,從而改進程式碼的可讀性。 在下面的示例方法中,sellerName
不得為 NULL 或空白符。 由於 sellerName
和 productName
都是字元串類型,所以使用命名實參而不是按位置發送實參是有意義的,可以區分這兩種類型並減少程式碼閱讀者的困惑。
當命名實參與位置實參一起使用時,只要
- 沒有後接任何位置實參或
PrintOrderDetails("Gift Shop", 31, productName: "Red Mug");
- 以 C# 7.2 開頭,則它們就有效並用在正確位置 。 在以下示例中,形參
orderNum
位於正確的位置,但未顯式命名。
PrintOrderDetails(sellerName: "Gift Shop", 31, productName: "Red Mug");
但是,如果其後接位置實參,則無序命名實參無效。
// 出現 CS1738 警告: 命名參數規範必須出現在指定了所有固定參數之後。 PrintOrderDetails(productName: "Red Mug", 31, "Gift Shop");
示例
以下程式碼執行本節以及某些其他節中的示例。
class NamedExample { static void Main(string[] args) { // 使用位置參數以常規方式調用方法。 PrintOrderDetails("Gift Shop", 31, "Red Mug"); // 可以按任意順序為參數提供命名參數。 PrintOrderDetails(orderNum: 31, productName: "Red Mug", sellerName: "Gift Shop"); PrintOrderDetails(productName: "Red Mug", sellerName: "Gift Shop", orderNum: 31); // 與位置參數混合的命名參數只要在正確的位置使用就有效 PrintOrderDetails("Gift Shop", 31, productName: "Red Mug"); PrintOrderDetails(sellerName: "Gift Shop", 31, productName: "Red Mug"); // C# 7.2 onwards PrintOrderDetails("Gift Shop", orderNum: 31, "Red Mug"); // C# 7.2 onwards // 但是,如果使用順序不對,則混合參數無效。 // 下列聲明會引起編譯錯誤 // PrintOrderDetails(productName: "Red Mug", 31, "Gift Shop"); // PrintOrderDetails(31, sellerName: "Gift Shop", "Red Mug"); // PrintOrderDetails(31, "Red Mug", sellerName: "Gift Shop"); } static void PrintOrderDetails(string sellerName, int orderNum, string productName) { if (string.IsNullOrWhiteSpace(sellerName)) { throw new ArgumentException(message: "Seller name cannot be null or empty.", paramName: nameof(sellerName)); } Console.WriteLine($"Seller: {sellerName}, Order #: {orderNum}, Product: {productName}"); } }
可選實參
方法、構造函數、索引器或委託的定義可以指定其形參為必需還是可選。 任何調用都必須為所有必需的形參提供實參,但可以為可選的形參省略實參。
每個可選形參都有一個默認值作為其定義的一部分。 如果沒有為該形參發送實參,則使用默認值。 默認值必須是以下類型的表達式之一:
- 常量表達式;
-
new ValType()
形式的表達式,其中ValType
是值類型,例如 enum 或 struct; - default(ValType) 形式的表達式,其中
ValType
是值類型。
可選參數定義於參數列表的末尾和必需參數之後。 如果調用方為一系列可選形參中的任意一個形參提供了實參,則它必須為前面的所有可選形參提供實參。 實參列表中不支援使用逗號分隔的間隔。 例如,在以下程式碼中,使用一個必選形參和兩個可選形參定義實例方法 ExampleMethod
。
public void ExampleMethod(int required, string optionalstr = "default string",int optionalint = 10)
下面對 ExampleMethod
的調用會導致編譯器錯誤,原因是為第三個形參而不是為第二個形參提供了實參。
//anExample.ExampleMethod(3, ,4);
但是,如果知道第三個形參的名稱,則可以使用命名實參來完成此任務。
anExample.ExampleMethod(3, optionalint: 4);
IntelliSense 使用括弧表示可選形參,如下圖所示:

還可通過使用 .NET OptionalAttribute 類聲明可選參數。 OptionalAttribute 形參不需要默認值。
在以下示例中,ExampleClass
的構造函數具有一個可選形參。 實例方法 ExampleMethod
具有一個必選形參(required
)和兩個可選形參(optionalstr
和 optionalint
)。 Main
中的程式碼演示了可用於調用構造函數和方法的不同方式。
1 namespace OptionalNamespace 2 { 3 class OptionalExample 4 { 5 static void Main(string[] args) 6 { 7 // 實例 anexample 不發送構造函數可選參數的參數。 8 ExampleClass anExample = new ExampleClass(); 9 anExample.ExampleMethod(1, "One", 1); 10 anExample.ExampleMethod(2, "Two"); 11 anExample.ExampleMethod(3); 12 13 // 實例anoThereExample為構造函數的可選參數發送參數。 14 ExampleClass anotherExample = new ExampleClass("Provided name"); 15 anotherExample.ExampleMethod(1, "One", 1); 16 anotherExample.ExampleMethod(2, "Two"); 17 anotherExample.ExampleMethod(3); 18 19 // 以下語句產生編譯器錯誤。 20 21 // 必須為第一個參數提供參數,並且該參數必須是整數。 22 //anExample.ExampleMethod("One", 1); 23 //anExample.ExampleMethod(); 24 25 // 您不能在提供的參數中留下空白。 26 //anExample.ExampleMethod(3, ,4); 27 //anExample.ExampleMethod(3, 4); 28 29 // 可以使用命名參數使前一條語句工作。 30 anExample.ExampleMethod(3, optionalint: 4); 31 } 32 } 33 34 class ExampleClass 35 { 36 private string _name; 37 38 // 因為構造函數name的參數有一個指定給它的默認值,所以是可選的 39 public ExampleClass(string name = "Default name") 40 { 41 _name = name; 42 } 43 44 // 第一個參數(必需)沒有指定默認值。因此,它不是可選的。OptionalStr和OptionalInt都有分配給它們的默認值。它們是可選的。 45 public void ExampleMethod(int required, string optionalstr = "default string", 46 int optionalint = 10) 47 { 48 Console.WriteLine("{0}: {1}, {2}, and {3}.", _name, required, optionalstr, 49 optionalint); 50 } 51 } 52 53 // 輸出: 54 // Default name: 1, One, and 1. 55 // Default name: 2, Two, and 10. 56 // Default name: 3, default string, and 10. 57 // Provided name: 1, One, and 1. 58 // Provided name: 2, Two, and 10. 59 // Provided name: 3, default string, and 10. 60 // Default name: 3, default string, and 4. 61 62 }
COM 介面
命名實參和可選實參,以及對動態對象的支援和其他增強功能大大提高了與 COM API(例如 Office Automation API)的互操作性。
例如,Microsoft Office Excel 的 Range 介面中的 AutoFormat 方法有七個可選形參。 這些形參如下圖所示:

在 C# 3.0 以及早期版本中,每個形參都需要一個實參,如下例所示。
var excelApp = new Microsoft.Office.Interop.Excel.Application(); excelApp.Workbooks.Add(); excelApp.Visible = true; var myFormat = Microsoft.Office.Interop.Excel.XlRangeAutoFormat.xlRangeAutoFormatAccounting1; excelApp.get_Range("A1", "B4").AutoFormat(myFormat, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing);
但是,可以通過使用 C# 4.0 中引入的命名實參和可選實參來大大簡化對 AutoFormat
的調用。 如果不希望更改形參的默認值,則可以通過使用命名實參和可選實參來為可選形參省略實參。 在下面的調用中,僅為 7 個形參中的其中一個指定了值。
excelApp.Range["A1", "B4"].AutoFormat( Format: myFormat );
有關詳細資訊和示例,請參閱操作說明:在 Office 編程中使用命名參數和可選參數和操作說明:使用 Visual C# 功能訪問 Office 互操作對象。
重載決策
使用命名實參和可選實參將在以下方面對重載決策產生影響:
- 如果方法、索引器或構造函數的每個參數是可選的,或按名稱或位置對應於調用語句中的單個自變數,且該自變數可轉換為參數的類型,則方法、索引器或構造函數為執行的候選項。
- 如果找到多個候選項,則會將用於首選轉換的重載決策規則應用於顯式指定的自變數。 將忽略可選形參已省略的實參。
- 如果兩個候選項不相上下,則會將沒有可選形參的候選項作為首選項,對於這些可選形參,已在調用中為其省略了實參。 這是重載決策中的常規引用的結果,該引用用於參數較少的候選項。