談談C# 4.0新特性“缺省參數”的實現
C#4.0關于缺省參數的新特性,相信大家都不會陌生。所謂缺省參數,顧名思義,就是在聲明方法的某個參數的時候為之指定一個默認值,在調用該方法的時候如果采用該默認值,你就無須指定該參數。和很多語言層面特性(語法糖)的實現一樣,缺省參數也是編譯器為我們玩的一個小花招。缺省參數最終體現為兩個特殊的自定義特性OptionalAttribute和DefaultParameterValueAttribute 。
目錄
一、缺省參數的用法
二、實現缺省參數的兩個特性:OptionalAttribute和DefaultParameterValueAttribute
三、直接通過OptionalAttribute和DefaultParameterValueAttribute 定義缺省參數
一、缺省參數的用法
比如下面一個TestMethod方法,后面兩個參數bar和baz就是缺省參數,默認值分別為“Bar”和“Baz”。
{
Console.WriteLine("{0, -5} - {1, -5} - {2, -5}", foo, bar, baz);
}
在調用TestMethod的時候,我們自由地選擇采用缺省的參數值,或者覆蓋該缺省值。
{
TestMethod("Foo");
TestMethod("Foo", "Bar1");
TestMethod("Foo", "Bar1", "Baz1");
}
下面是輸出結果:
Foo - Bar1 - Baz
Foo - Bar1 - Baz1
缺省參數的使用有兩個簡單的限制,其一是:缺省參數的聲明只能放在普通參數之后。如下代碼中定義的TestMethod方法中,缺省參數bar后面跟一個非缺省參數baz,這樣的代碼是不能通過編譯的(編譯錯誤信息為:Optional parameters must appear after all required parameters)。
{
Console.WriteLine("{0, -5} - {1, -5} - {2, -5}", foo, bar, baz);
}
但是,缺省參數后面可以跟數組參數(params參數),實際上無論在什么情況下,params參數都只能是最后一個聲明的參數。關于缺省參數的聲明的位置限制,主要重載方法的識別機制決定的,這一點大家都很容易理解。
缺省參數的另一個限制是:指定的缺省值必須是一個常量,這就實際上為作為缺省參數的數據類型作了限制——只能是系統定義的基元類型。下面定義的TestMethod方法中,我們定義了一個DateTime類型的缺省參數,并將參數缺省值作為DateTime.Now。由于DateTime.Now不是常量,所以這樣的代碼也不能通過編譯(編譯錯誤消息:Default parameter value for 'date' must be a compile-time constant)。
{
//Others...
}
二、實現缺省參數的兩個特性:OptionalAttribute和DefaultParameterValueAttribute
為什么缺省參數的默認值只能接受常量呢?如果你了解了缺省參數的本質,這就不是一個問題。那么缺省參數究竟是如何實現的呢?
和很多語言層面特性(語法糖)的實現一樣,缺省參數也是編譯器為我們玩的一個小花招,而真正編譯后的東西都是我們再熟悉不過的玩意兒。當包含缺省參數的C#代碼經過編譯后,缺省參數體現在兩個特殊的自定義特性OptionalAttribute和DefaultParameterValueAttribute 。前者將參數標識為缺省參數,后者指定其缺省值。
public sealed class OptionalAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Parameter)]
public sealed class DefaultParameterValueAttribute : Attribute
{
public DefaultParameterValueAttribute(object value);
public object Value {get; }
}
對于最開始我們定義的TestMethod方法,編譯后的形式如下所示。
[Optional, DefaultParameterValue("Bar")] string bar,
[Optional, DefaultParameterValue("Baz")] string baz)
{
//Others..
}
正是因為缺省參數的默認值最終是作為DefaultParameterValueAttribute的參數存在的,所以它必須是常量。
三、直接通過OptionalAttribute和DefaultParameterValueAttribute 定義缺省參數
既然缺省參數最終體現為OptionalAttribute和DefaultParameterValueAttribute 這兩個特性,我們是否可以直接通過它們來定義缺省參數呢?答案是:當然可以,下面的代碼一樣可以正常執行。
{
TestMethod("Foo");
TestMethod("Foo","Bar1");
TestMethod("Foo","Bar1","Baz1");
}
private static void TestMethod(string foo,
[Optional, DefaultParameterValue("Bar")] string bar,
[Optional, DefaultParameterValue("Baz")] string baz)
{
//Others..
}
如果調用含有缺省參數的方法,并且沒有顯示指定該參數,編譯器在編譯的時候會自動將默認值附加上去。對于上面的Main方法,下面是與之等效的編譯后代碼。
{
TestMethod("Foo", "Bar", "Baz");
TestMethod("Foo", "Bar1", "Baz");
TestMethod("Foo", "Bar1", "Baz1");
}
雖然說我們通過OptionalAttribute和DefaultParameterValueAttribute 這兩個特性也可以定義缺省參數,但是當我們將缺省參數定義在普通參數之前是,編譯器不會報錯。倒是方法中缺省參數實際上就相當于普通參數了。
{
//TestMethod("Foo","Baz");
//上面的方法調用無效
TestMethod("Foo","Bar1","Baz1");
}
private static void TestMethod(string foo,
[Optional, DefaultParameterValue("Bar")] string bar,
string baz)
{
//Others..
}