理解動態類型
C#是一種靜態類型的語言,靜態類型能夠讓編譯器幫你找出更多的錯誤,因為編譯器能夠在編譯時進行大部分的檢查工作。
C#動態類型是為了讓靜態代碼能夠更加平滑地與其他使用動態類型的環境進行交互,如動態地使用COM、調用IronPython代碼及簡化反射操作。
當你需要不知道具體類型的運行時解析方法的時候,動態類型是最佳的工具。
如果你能在編譯期間明確類型,那么可以使用 lambda 表達式和函數式編程來解決問題。
優先使用靜態類型,靜態類型比動態類型更高效,動態類型和在運行時創建表達式樹都會帶來性能上的影響。
限制:擴展方法不能基於動態對象定義。在調用一個動態數據的方法時,不能使用Lambda 表達式和C#匿名方法;
#region Lambda 匿名方法 擴展方法不能用於動態數據 static void DynamicLambda() { dynamic dyn = new Person(); //匿名方法 dyn.LamdbaMethod(delegate (string str) { Console.WriteLine(str); }); //lambda dyn.LamdbaMethod(str => Console.WriteLine(str)); //只能使用基本委托方式 TestDynamic<string> testDyn = new TestDynamic<string>(str => Console.WriteLine(str)); dyn.LamdbaMethod(testDyn); //擴展方法 //var data = from d in dyn select d; } #endregion
動態對象的數據類型可以不斷變化:
#region Change a dynamic local variable static void ChangeDynamicDataType() { // Declare a single dynamic data point // named 't'. dynamic t = "Hello!"; Console.WriteLine("t is of type: {0}", t.GetType()); t = false; Console.WriteLine("t is of type: {0}", t.GetType()); t = new List<int>(); Console.WriteLine("t is of type: {0}", t.GetType()); } #endregion
DLR(Dynamic Language Runtime):允許添加動態語言(Ruby Python),並使C#具備和這些動態語言相同的某些動態功能(C#4及以上)。使不同語言的互操作變得簡單;
DLR位於System.Dynamic名稱空間和System.Runtime.ComplierServices名稱空間中;程序集 System.Core.dll中;
表達式樹:一種DLR在運行時創建代碼的方法;大多數情況,可以使用 Lambda 表達式創建泛型 API,讓調用者自己動態定義所需要執行的代碼。
/// <summary> /// Add,使用表達式樹 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="a"></param> /// <param name="b"></param> /// <returns></returns> private T AddExpressionTree<T>(T a, T b) { ParameterExpression leftOperand = Expression.Parameter(typeof(T), "left"); ParameterExpression rightOperand = Expression.Parameter(typeof(T), "right"); BinaryExpression body = Expression.Add(leftOperand, rightOperand); Expression<Func<T, T, T>> adder = Expression.Lambda<Func<T, T, T>>(body, leftOperand, rightOperand); Func<T, T, T> theDelegate = adder.Compile(); return theDelegate(a, b); }
DLR在非特定條件下使用表達式樹來獲取動態調用的含義,將表達式樹傳遞給目標對象(COM接口的IDispatch,動態語言的IDynamicObject,.Net對象的映射)
反射(后期綁定)使用動態類型:簡化必須手動使用MethodInfo類、手動查詢元數據;使用dynamic簡化傳遞參數(直接使用參數而不需要使用數組);
private static void AddWithDynamic() { Assembly asm = Assembly.Load("MathLibrary"); try { // Get metadata for the SimpleMath type. Type math = asm.GetType("MathLibrary.SimpleMath"); // Create a SimpleMath on the fly. dynamic obj = Activator.CreateInstance(math); Console.WriteLine("Result is: {0}", obj.Add(10, 70)); } catch (Microsoft.CSharp.RuntimeBinder.RuntimeBinderException ex) { Console.WriteLine(ex.Message); } }
COM互操作程序集的簡化:.Net和COM之間的交互在后台通過Runtime Callable Wrapper(RCW)對數據進行封送;RCW是一個動態生成的代理,它對.Net數據類型進行封裝並轉換為COM類型,調整COM對象的引用計數器,將COM的返回值映射為等價的.net類型;
主互操作程序集(PIA):是優化的互操作程序集,由發布者簽名的規范COM庫互操作程序集。一般都帶有 Interop關鍵字;
C#4提供對PIA引用的一種方式:鏈接(編譯器只會將PIA中需要的部分直接嵌入到程序集中),變體(variant)被視為動態類型,以減少強制轉換需要的開銷;
創建帶有動態功能的類型的最簡單的方法就是繼承 System.Dynamic.DynamicObject。還需要重寫 TryGetMemebr() 和 TrySetMemebr()。
實現 IDynamicMetaObjectProvider 就意味着需要實現方法 GetmetaObject()。
創建動態類型時首選繼承,如果必須使用其他基類,可以手工實現 IDynamicMetaObjectProvider 接口;
有些函數是不能通過動態綁定調用的,會產生一個RuntimeBinderException異常;它們是:
1. 擴展方法(通過擴展方法語法調用)
2. 接口成員(特指顯示實現的接口成員)
3. 受可訪問性限制的方法