程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> C#4.0新特性(2):Named and Optional Arguments 命名參數和可選參數

C#4.0新特性(2):Named and Optional Arguments 命名參數和可選參數

編輯:C#入門知識

為什麼需要開放命名參數和可選參數呢?
•這是出於動態語言運行時兼容性的要求。動態語言中存在動態綁定的參數列表,有時候並不是所有的參數值都需要指定(有些語言可能沒有重載決策);
•另外,在一些 COM 互操作時,往往 COM Invoke 的方法參數列表非常的長(例如 ExcelApplication.Save,可能需要 12 個參數),但 COM 暴露的參數的實際值往往為 null,只有很少一部分參數需要指定植,如 ExcelApplication.Save(),可能不需要指定任何參數值,或者僅僅一個值,例如 fileType。為了精簡書寫的代碼,就有著這個特性。

命名參數和可選參數是兩個截然不同的功能,但通常一起使用。在進行成員調用時,可以忽略可選參數;而命名參數的方式可以通過名稱來提供一個參數,而無需依賴它在參數列表中出現的位置。

有些API——尤其是COM接口——如Office自動化API——確實本身就是通過命名參數和可選參數編寫的。之前在C#中調用這些API非常痛苦,尤其有的時候需要多達30幾個參數都必須顯式傳遞,而其中大多數都具有合理的默認值,是可以忽略的。

即便是編寫.NET中的API,你也會發現很多時候你在被迫為不同的參數組合方式編寫一個方法的大量重載形式,以便給調用者提供最高的可用性。在這種情況下,可選參數就會成為一種非常有用的替代方式。
Named parameters 命名參數
有了命名實參,您將不再需要記住或查找形參在所調用方法的形參列表中的順序。 可以按形參名稱指定每個實參的形參。 例如,可以采用標准方式調用計算身體質量指數 (BMI) 的函數,方法是依照該函數定義的順序按位置發送體重和身高的實參。

CalculateBMI(123, 64);

如果不記得形參的順序,但卻知道其名稱,您可以按任意順序(先發送體重或先發送身高)發送實參。

CalculateBMI(weight: 123, height: 64);

CalculateBMI(height: 64, weight: 123);

命名實參還可以標識每個實參所表示的含義,從而改進代碼的可讀性。

命名實參可以放在位置實參後面,如此處所示。

CalculateBMI(123, height: 64);

但是,位置實參不能放在命名實參後面。 下面的語句會導致編譯器錯誤。

//CalculateBMI(weight: 123, 64);

01 class NamedExample

02 {

03     static void Main(string[] args)

04     {

05         // The method can be called in the normal way, by using positional arguments.

06         Console.WriteLine(CalculateBMI(123, 64));

07  

08         // Named arguments can be supplied for the parameters in either order.

09         Console.WriteLine(CalculateBMI(weight: 123, height: 64));

10         Console.WriteLine(CalculateBMI(height: 64, weight: 123));

11  

12         // Positional arguments cannot follow named arguments.

13         // The following statement causes a compiler error.

14         //Console.WriteLine(CalculateBMI(weight: 123, 64));

15  

16         // Named arguments can follow positional arguments.

17         Console.WriteLine(CalculateBMI(123, height: 64));

18     }

19  

20     static int CalculateBMI(int weight, int height)

21     {

22         return (weight * 703) / (height * height);

23     }

24 }
 Named Parameters 的實質就是:
命名參數,無非是變相告知參數鍵值而已,最終編譯結果還是按照原有的規則和順序生成方法調用。這只不過是中語法糖而已。

Optional parameters 可選參數
為一個參數提供默認值就可以將其聲明為可選的——

1 public void M(int x, int y = 5, int z = 7);
這裡的y和z就是可選參數,在調用時可以忽略——

1 M(1, 2, 3); // ordinary call of M 

2 M(1, 2); // omitting z – equivalent to M(1, 2, 7) 

3 M(1); // omitting both y and z – equivalent to M(1, 5, 7)
方法、構造函數、索引器或委托的定義可以指定其形參為必需還是可選。 任何調用都必須為所有必需的形參提供實參,但可以為可選的形參省略實參。

每個可選形參都具有默認值作為其定義的一部分。 如果沒有為該形參發送實參,則使用默認值。 默認值必須是一個表達式的以下類型之一:

•常數表達式;
•窗體 new ValType()的表達式, ValType 是值類型,例如 枚舉 或 結構;
•窗體 默認 (ValType)的表達式, ValType 是值類型。
可選形參在形參列表的末尾定義,位於任何必需的形參之後。 如果調用方為一系列可選形參中的任意一個形參提供了實參,則它必須為前面的所有可選形參提供實參。 實參列表中不支持使用逗號分隔的間隔。 例如,在以下代碼中,使用一個必選形參和兩個可選形參定義實例方法 ExampleMethod。

1 public void ExampleMethod(int required, string optionalstr = "default string", int optionalint = 10)
下面對 ExampleMethod 的調用導致編譯器錯誤,原因是為第三個形參而不是為第二個形參提供了實參。

1 //anExample.ExampleMethod(3, ,4);
但是,如果您知道第三個形參的名稱,則可以使用命名實參來完成任務。

1 anExample.ExampleMethod(3, optionalint: 4);
IntelliSense 使用括號指示可選形參,如下圖所示。

 

還可以通過使用 .NET OptionalAttribute 類來聲明可選形參。 OptionalAttribute 形參不需要默認值。

在下面的示例中,ExampleClass 的構造函數具有一個形參,該形參是可選的。 實例方法 ExampleMethod具有一個必需的形參:required,以及兩個可選形參:optionalstr 和 optionalint。 Main 中的代碼演示了可用於調用構造函數和方法的不同方式。

01 namespace OptionalNamespace

02 {

03     class OptionalExample

04     {

05         static void Main(string[] args)

06         {

07             // Instance anExample does not send an argument for the constructor's

08             // optional parameter.

09             ExampleClass anExample = new ExampleClass();

10             anExample.ExampleMethod(1, "One", 1);

11             anExample.ExampleMethod(2, "Two");

12             anExample.ExampleMethod(3);

13  

14             // Instance anotherExample sends an argument for the constructor's

15             // optional parameter.

16             ExampleClass anotherExample = new ExampleClass("Provided name");

17             anotherExample.ExampleMethod(1, "One", 1);

18             anotherExample.ExampleMethod(2, "Two");

19             anotherExample.ExampleMethod(3);

20  

21             // The following statements produce compiler errors.

22  

23             // An argument must be supplied for the first parameter, and it

24             // must be an integer.

25             //anExample.ExampleMethod("One", 1);

26             //anExample.ExampleMethod();

27  

28             // You cannot leave a gap in the provided arguments.

29             //anExample.ExampleMethod(3, ,4);

30             //anExample.ExampleMethod(3, 4);

31  

32             // You can use a named parameter to make the previous

33             // statement work.

34             anExample.ExampleMethod(3, optionalint: 4);

35         }

36     }

37  

38     class ExampleClass

39     {

40         private string _name;

41  

42         // Because the parameter for the constructor, name, has a default

43         // value assigned to it, it is optional.

44         public ExampleClass(string name = "Default name")

45         {

46             _name = name;

47         }

48  

49         // The first parameter, required, has no default value assigned

50         // to it. Therefore, it is not optional. Both optionalstr and

51         // optionalint have default values assigned to them. They are optional.

52         public void ExampleMethod(int required, string optionalstr = "default string",

53             int optionalint = 10)

54         {

55             Console.WriteLine("{0}: {1}, {2}, and {3}.", _name, required, optionalstr,

56                 optionalint);

57         }

58     }

59  

60     // The output from this example is the following:

61     // Default name: 1, One, and 1.

62     // Default name: 2, Two, and 10.

63     // Default name: 3, default string, and 10.

64     // Provided name: 1, One, and 1.

65     // Provided name: 2, Two, and 10.

66     // Provided name: 3, default string, and 10.

67     // Default name: 3, default string, and 4.

68  

69 }
Optional Parameters 的實質就是:
編譯器為可選參數增加了 OptionalAttribute 和 DefaultParameterValueAttribute 特性,以便於引用編譯和反射調用時能獲取默認值。
至於不用 OptionalAttribute 和 DefaultParameterValueAttribute 特性的寫法只不過是種語法糖而已。

Named and optional arguments 命名的和可選的實參
C# 4.0不允許忽略逗號之間的實參,比如M(1,,3)。否則會導致大量不可讀的、需要“數逗號”的代碼。替代方式是任何參數都可以通過名字傳遞。因此如果在調用M時只希望忽略y,可以寫——

1 M(1, z: 3); // passing z by name

1 M(x: 1, z: 3); // passing both x and z by name

1 M(z: 3, x: 1); // reversing the order of arguments
這幾種形式都是等價的,不過參數總是按照其出現的順序進行求值,因此對於最後一個示例來說,3會在1之前求值。

可選參數和命名參數不僅可以用在方法調用中,還可以用在索引器和構造器中。

COM接口
命名實參和可選實參,以及對動態對象的支持和其他增強功能大大提高了與 COM API(例如 Office 自動化 API)的互操作性。

例如,Microsoft Office Excel 的 Range 接口中的 AutoFormat 方法具有七個形參,這七個形參都是可選的。這些形參如下圖所示。

AutoFormat 形參


在 C# 3.0 和早期版本中,每個形參都需要一個實參,如以下示例所示。

01 // In C# 3.0 and earlier versions, you need to supply an argument for

02 // every parameter. The following call specifies a value for the first

03 // parameter, and sends a placeholder value for the other six. The

04 // default values are used for those parameters.

05 var excelApp = new Microsoft.Office.Interop.Excel.Application();

06 excelApp.Workbooks.Add();

07 excelApp.Visible = true;

08  

09 var myFormat =

10     Microsoft.Office.Interop.Excel.XlRangeAutoFormat.xlRangeAutoFormatAccounting1;

11  

12 excelApp.get_Range("A1", "B4").AutoFormat(myFormat, Type.Missing,

13     Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing);
但是,可以通過使用 C# 4.0 中引入的命名實參和可選實參來大大簡化對 AutoFormat 的調用。 如果不希望更改形參的默認值,則可以通過使用命名實參和可選實參來為可選形參省略實參。 在下面的調用中,僅為七個形參中的其中一個指定了值。

1 // The following code shows the same call to AutoFormat in C# 4.0. Only

2 // the argument for which you want to provide a specific value is listed.

3 excelApp.Range["A1", "B4"].AutoFormat( Format: myFormat );
 

Overload resolution 重載解析
命名參數和可選參數影響了重載解析,但產生的變化相當簡單——

如果所有的參數或者是可選的,或者在調用時(通過名字或位置)明確提供了對應的實參,並且實參能夠轉換為形參類型,則該簽名是可適用的(applicable)。

轉換的最優原則只用於明確給定的實參——出於最優的目的,忽略掉的可選參數在重載解析時將不做考慮。

如果兩個簽名一樣好,則沒有忽略可選參數的那個勝出。

1 M(string s, int i = 1); 

2 M(object o); 

3 M(int i, string s = “Hello”); 

4 M(int i); 

5  

6 M(5);
對於給定的這些重載,我們可以看看上述規則的工作方式。M(string,int)不是可適用的,因為5不能轉換為string。M(int,string)是可適用的,因為它的第二個參數是可選的,然後很明顯,M(object)和M(int)也是可適用的。

M(int,string)和M(int)都比M(object)要好,因為將5轉換為int優於將5轉換為object。

最後,M(int)優於M(int,string),因為它沒有被忽略的可選參數。

因此,最終調用的方法是M(int)。

使用命名實參和可選實參將在以下方面對重載決策產生影響:

•如果方法、索引器或構造函數的各個形參均為可選,或者按名稱或位置與調用語句中的單個實參對應,並且該實參可轉換為形參的類型,則該方法、索引器或構造函數是執行的候選項。
•如果找到多個候選項,則會將首選轉換的重載決策規則應用於顯式指定的實參。 將忽略可選形參已省略的實參。
•如果兩個候選項不相上下,則會將沒有可選形參的候選項作為首選項,對於這些可選形參,已在調用中為其省略了實參。 這是具有較少形參的候選項的重載決策中一般首選項的結果。


 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved