C#4.0
C#4.0
動態繫結
C#4.0
引入了一個新的關鍵字dynamic
,用來表示動態型別。dynamic
的出現讓C#具有了弱語言型別的特性。
關於dynamic的主要規則:
- 幾乎所有CLR型別都可以隱式轉換為dynamic
- 所有dynamic型別的表示式都可以隱式轉換為CLR型別
- 使用dynamic型別值得表示式通常會動態的求值
- 動態求值表示式的靜態型別通常被視為dynamic
編譯器對待dynamic
的方式與普通的CLR型別不同。任何使用了動態值的表示式都會從根本上改變編譯器的行為。編譯器不會試圖弄懂程式碼的確切含義,不會恰當的繫結各個成員的訪問,不會執行過載決策。它只是通過解析原始碼,找出要執行的操作的種類、名稱、所設計的引數以及其他相關資訊。編譯器也不會發出(emit)IL來直接執行程式碼,而是使用所有必要的資訊生成呼叫DLR的程式碼。剩下的工作將在執行時進行。
DLR(Dynamic Language Runtime),動態語言執行時,是所有動態語言和C#編譯器用來動態執行程式碼的庫。
DLR雖然以執行時為名,但與CLR不在同一個級別(它不涉及JIT編譯、本地API奉送、GC等內容)。而是建立在大量.NET 2.0
和 .NET 3.5
的功能之上,特別是DynamicMethod
和Expression
型別。.NET4
還擴充套件了表示式樹的API,DLR可以使用它來表示更多的概念。
儘管DLR不直接操作原生代碼,但在在某種程度上我們可以認為它做著與CLR類似的工作:正如CLR將IL轉換為原生代碼一樣,DLR將用繫結器、呼叫點、元物件以及其他各種概念表示的程式碼轉換為表示式樹,然後將表示式樹編譯為IL,最終由CLR編譯為原生代碼。
不要混淆dynamic
和var
。用var
宣告區域性變數只是一種簡化語法,它要求編譯器根據表示式推斷出具體資料型別。var
關鍵字只能在方法內部宣告區域性變數,而dynamic
關鍵字可用於區域性變數、欄位和引數。表示式不能轉型為var
,但能轉型為dynamic
。必須顯式初始化用var
宣告的變數,但無需初始化用dynamic
宣告的變數。
命名實參/可選引數
命名實參
named argument
有了命名實參,你將不再需要記住或查詢形參在所呼叫方法的形參列表中的順序。 每個實參的形參都可按形參名稱進行指定。
SetName(firstName:"first",middleName:'middle',LastName:'last'); //可以按任意順序設定實參 SetName(LastName:'last',firstName:"first",middleName:'middle');
可選引數
optional parameter
方法、建構函式、索引器或委託的定義可以指定其形參為必需還是可選。 任何呼叫都必須為所有必需的形參提供實參,但可以為可選的形參省略實參。
每個可選形參都有一個預設值作為其定義的一部分。 如果沒有為該形參傳送實參,則使用預設值。
可選引數意味著一些引數是可選的,呼叫者不必顯式指定它們的值,但是必須給可選引數一個預設值。預設值必須為數字或字串字面量、null、const常量、列舉成員和default(T)操作符。
可選引數必須出現在必需引數之後。
命名實參常常與可選引數同時出現。
public void ExampleMethod(int required, string optionalstr = "default string",
int optionalint = 10)
{
}
//呼叫
anExample.ExampleMethod(3, optionalint: 4);
泛型協變和逆變
可變性是指以一種型別安全的方式,將一個物件作為另一個物件來使用。
可變性分為兩種:
- 協變性(Covariance):指返回型別的相容性,用於向呼叫者返回某項操作的值,能夠使用比原始指定的派生型別的派生程度更大(更具體的)的型別。
- 逆變性(Contravariance):指引數的相容性,用於呼叫者向某項操作傳入的值,指能夠使用比原始指定的派生型別的派生程度更小(不太具體的,更抽象的)的型別。
可變性摘要:
- 在
.NET Framework 4
中,可變性引數僅限於泛型介面
和泛型委託型別
。 - 泛型介面或泛型委託型別可以同時具有協變和逆變型別引數。
- 可變性引數僅適用於引用型別;如果為可變性引數指定值型別,則該型別引數對於生成的構造型別是不變的。
- 可變性引數不適用於委託組合。
在泛型介面或委託的宣告中,C#4使用out
修飾符來指定型別引數的協變性,使用in
修飾符來指定逆變性。宣告完成後,就可以對相關的型別進行隱式轉換了。
如果有一個泛型引數標記為out
,則代表它是用來輸出的,只能作為結果返回;而如果有一個泛型引數標記為in
,則代表它是用來輸入的,只能作為引數傳入。
可變性的轉換是引用轉換:任何使用了協變和逆變的轉換都是引用轉換,這意味著轉換之後將返回相同的引用。它不會建立新的物件,只是認為現有的引用與目標型別匹配。
我們熟知的Action<T>
和Func<T>
就分別使用了逆變跟協變。
//泛型引數具有逆變性
public delegate void Action<in T>(T obj);
public interface IComparer<in T>
{
int Compare(T x, T y);
}
//泛型引數具有協變性
public delegate T Func<out T>();
public interface IEnumerable<out T> : IEnumerable
{
new IEnumerator<T> GetEnumerator();
}
//**************逆變性***************
//明明object不可以轉換為string,為啥strComparer可以接收IComparer<object>型別的變數?
//是因為IComparer<in T>的泛型引數具有逆變性
//所以IComparer<string>介面可以使用比string更抽象的object型別
IComparer<object> objComparer = null;
IComparer<string> strComparer = objComparer;
//同理,因為Action<in T>的泛型引數具有逆變性
//所以Action<string>委託可以使用比string更抽象的object型別
Action<object> objAction = (obj) => Console.WriteLine(obj.GetHashCode());
Action<string> strAction = objAction;
//**************協變性***************
//由於IEnumerable<out T>的泛型引數具有協變性
//所以IEnumerable<object>介面可以使用比object更具體的string型別
IEnumerable<string> strEnumerable = new List<string> { };
IEnumerable<object> objEnumerable = strEnumerable;
//同理,因為Func<out T>的泛型引數具有協變性
//所以Func<object>委託可以使用比object更具體的string型別
Func<string> strFunc = () => { return ""; };
Func<object> objFunc = strFunc;
嵌入的互操作型別
從 .NET Framework 4
開始,公共語言執行時支援將 COM
型別的型別資訊直接嵌入到託管程式集中,而不要求託管程式集從互操作程式集中獲取 COM
型別的型別資訊。 由於嵌入式型別資訊僅包含託管程式集實際使用的型別和成員,因此兩個託管程式集可能具有相同 COM
型別的不同檢視。 每個託管程式集都有不同的 Type
物件來表示其 COM
型別檢視。 公共語言執行時支援介面、結構、列舉和委託等不同檢視之間的型別等效性。
型別等效性意味著從一個託管程式集傳遞到另一個託管程式集的 COM
物件可以轉換為接收程式集中適當的託管型別。
備註:型別等效性和嵌入式互操作型別簡化了使用
COM
元件的應用程式和載入項的部署,因為無需與應用程式一起部署互操作程式集。 如果共享COM
元件的開發人員希望較早版本的.NET Framework
使用其元件,他們仍須建立主互操作程式集 (PIA
)。