1. 程式人生 > 實用技巧 >C#4.0

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 的功能之上,特別是DynamicMethodExpression型別。.NET4還擴充套件了表示式樹的API,DLR可以使用它來表示更多的概念。

儘管DLR不直接操作原生代碼,但在在某種程度上我們可以認為它做著與CLR類似的工作:正如CLR將IL轉換為原生代碼一樣,DLR將用繫結器、呼叫點、元物件以及其他各種概念表示的程式碼轉換為表示式樹,然後將表示式樹編譯為IL,最終由CLR編譯為原生代碼。

不要混淆dynamicvar。用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)。