C#(99):C# 4.0 新特性(.NET Framework 4.0 與 Visual Studio 2010 )


一、dynamic binding:動態綁定

在通過 dynamic 類型實現的操作中,該類型的作用是不在編譯時類型檢查,而是在運行時解析這些操作。dynamic 類型簡化了對 COM API(例如 Office Automation API)、動態 API(例如 IronPython 庫)和 HTML 文檔對象模型 (DOM) 的訪問。

1、簡化反射

在3.0及之前,如果你不知道一個變量的類型,而要去調用它的一個方法,一般會用到反射:

object calc = GetCalculator();
Type calcType = calc.GetType();object res = calcType.InvokeMember("Add",BindingFlags.InvokeMethod, null,new object[] { 10, 20 }); int sum = Convert.ToInt32(res);

有了dynamic,就可以把上面代碼簡化為:

dynamic calc = GetCalculator();
int sum = calc.Add(10, 20);

2、dynamic可用在變量、字段、屬性、方法參數和返回值等地方。

void Main()
{
    dynamic dynamic_ec = new ExampleClass();
    Console.WriteLine(dynamic_ec.exampleMethod(10));
}

class ExampleClass
{
    static dynamic field;// 動態字段
    dynamic prop { get; set; }//動態屬性.
    public dynamic exampleMethod(dynamic d)// 動態返回類型和動態參數
    {
        dynamic local = "Local variable";   // 動態局部變量
        int two = 2;

        if (d is int)
        {
            return local;
        }
        else
        {
            return two;
        }
    }
}

3、dynamic用在變量的傳遞中注意事項

dynamic也可以用在變量的傳遞中,runtime會自動選擇一個最匹配的重載方法。

調用含有明確類型參數和含有dynamic 類型的重載函數時,優先調用的是明確定義的類型。當沒有匹配的類型的時候,才會調用含有dynamic類的重載函數。

static void Main(string[] args)
{
    int i1 = 3;
    f(i1);

    double i2 = 1;
    f(i2);
}

static public void f(dynamic d)
{
    Console.WriteLine("dynamic ");
    Console.WriteLine(d);
    Console.WriteLine(d.GetType().ToString());
}

static public void f(int i)
{
    Console.WriteLine("int ");
    Console.WriteLine(i);
    Console.WriteLine(i.GetType().ToString());
}

4、dynamic的實現是基於IDynamicObject接口和DynamicObject抽象類。

直接使用該類型,可以非常方便的插入屬性, 方法。

dynamic person = new System.Dynamic.ExpandoObject();
person.id = 1;
person.title = "aa";
person.url = "bb.com";
person.co = "cc";
person.des = new Func<string>(() => person.title +person.url);
Console.Write(person.des());//結果aabb.com

其中動態方法、屬性的調用都被轉為了GetMember、Invoke等方法的調用。

public abstract class DynamicObject : IDynamicObject
{
    public virtual object GetMember(GetMemberBinder info);
    public virtual object SetMember(SetMemberBinder info, object value);
    public virtual object DeleteMember(DeleteMemberBinder info); public virtual object UnaryOperation(UnaryOperationBinder info);
    public virtual object BinaryOperation(BinaryOperationBinder info, object arg);
    public virtual object Convert(ConvertBinder info); public virtual object Invoke(InvokeBinder info, object[] args);
    public virtual object InvokeMember(InvokeMemberBinder info, object[] args);
    public virtual object CreateInstance(CreateInstanceBinder info, object[] args); public virtual object GetIndex(GetIndexBinder info, object[] indices);
    public virtual object SetIndex(SetIndexBinder info, object[] indices, object value);
    public virtual object DeleteIndex(DeleteIndexBinder info, object[] indices); public MetaObject IDynamicObject.GetMetaObject();
}

5、var和dynamic之間的區別

  1. 聲明的變量
    關鍵字var是在C#3.0中引入的,聲明的變量是靜態類型的,變量的類型由編譯器在編譯時決定。
    關鍵字dynamic是在C#4.0中引入的,聲明的變量是動態類型的,變量的類型由編譯器在運行時決定。
  2. 初始化
    關鍵字var聲明的變量應在聲明時初始化,這樣編譯器就會根據初始化的值來決定變量的類型。如果變量未初始化,則拋出錯誤。
    關鍵字dynamic聲明的變量在聲明時不需要初始化此類型的變量,因為編譯器在編譯時不知道變量的類型。如果變量未初始化,也不會拋出錯誤。
  3. intelliSense的支持
    關鍵字var支持visual studio中的intelliSense。關鍵字dynamic不支持visual studio中的intelliSense
  4. 應用
    關鍵字var不能用於屬性或從函數返回值。它只能用作函數中的局部變量。
    關鍵字dynamic可以用於屬性或從函數返回值。

6、用dynamic增強C#泛型表達力

除了以下運算符重載,對於普通的方法調用也是適用的。這種方法是一種動態duck typing的泛型參數約束機制,依賴於運行時的方法查找,與模板編譯時的檢查不同,它需要使用者保證傳入的對象符合相應要求。

static class Calculator
{
    public static T Add<T>(T t1, T t2)
    {
       dynamic d1 = t1;
        dynamic d2 = t2;
        return (T)(d1 + d2);
    }
}

public static void Main(string[] args)
{
    int i = Calculator.Add(1, 2);
    string s = Calculator.Add("abc", "def");
    Console.WriteLine(i + " " + s);
}

二、Named and optional arguments:命名參數和可選參數

1、帶有可選參數方法的聲明:

public StreamReader OpenTextFile(string path, 
Encoding encoding = null, bool detectEncoding = true, int bufferSize = 1024
);

2、命名參數必須放在最后:

OpenTextFile("foo.txt", Encoding.UTF8, 
bufferSize:
 4096);

3、命名參數順序不限:

OpenTextFile(
bufferSize:
4096, 
path:
 "foo.txt", 
detectEncoding:
 false);

三、Generic co- and contravariance:泛型的協變和逆變

在C#中,下面的類型轉換是非法的:

IList<string> strings = new List<string>();
IList<object> objects = strings;

因為你有可能會這樣做,而編譯器的靜態檢查無法查出錯誤:

objects[0] = 5;
string s = strings[0];

4.0中在聲明generic的Interface及Delegate時可以加in及out關鍵字,如:

public interface IEnumerable<out T> : IEnumerable
{
   IEnumerator<T> GetEnumerator();
}
public interface IEnumerator<out T> : IEnumerator
{
   bool MoveNext();
   T Current { get; }
}

public interface IComparer<in T>
{
 public int Compare(T left, T right);
}
  • out關鍵字的意思是說IEnumerable<T>中T只會被用在輸出中,值不會被改變。這樣將IEnumerable<string>轉為IEnumerable<object>類型就是安全的。
  • in的意思正好相反,是說IComparer<T>中的T只會被用在輸入中,這樣就可以將IComparer<object>安全的轉為IComparer<string>類型。

前者被稱為Co-Variance協變, 后者就是Contra-Variance逆變 。

.Net4.0中使用out/in聲明的Interface:

System.Collections.Generic.IEnumerable<out T>
System.Collections.Generic.IEnumerator<out T>
System.Linq.IQueryable<out T>
System.Collections.Generic.IComparer<in T>
System.Collections.Generic.IEqualityComparer<in T>
System.IComparable<in T>

Delegate:

System.Func<in T, …, out R>
System.Action<in T, …>
System.Predicate<in T>
System.Comparison<in T>
System.EventHandler<in T>

四、Embedded interop types (“NoPIA”):開啟嵌入類型信息,增加引用COM組件程序的中立性

在C#中在調用COM對象如office對象時,經常需要寫一堆不必要的參數:

object fileName = "Test.docx";
object missing  = System.Reflection.Missing.Value;
doc.SaveAs(ref fileName,
ref missing, ref missing, ref missing,
ref missing, ref missing, ref missing,
ref missing, ref missing, ref missing,
ref missing, ref missing, ref missing,
ref missing, ref missing, ref missing);

4.0中就可以直接寫成:

doc.SaveAs("Test.docx");

C#4.0對COM交互做了下面幾方面的改進:

  1. Automatic object -> dynamic mapping
  2. Optional and named parameters
  3. Indexed properties
  4. Optional “ref” modifier
  5. Interop type embedding (“No PIA”)

對第1點和第5點的簡單解釋如下:

在COM調用中,很多輸入輸出類型都是object,這樣就必須知道返回對象的確切類型,強制轉換后才可以調用相應的方法。在4.0中有了dynamic的支持,就可以在導入這些COM接口時將變量定義為dynamic而不是object,省掉了強制類型轉換。

PIA(Primary Interop Assemblies)是根據COM API生成的.Net Assembly,一般體積比較大。在4.0中運行時不需要PIA的存在,編譯器會判斷你的程序具體使用了哪一部分COM API,只把這部分用PIA包裝,直接加入到你自己程序的Assembly里面。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM