C#學習筆記(十一):動態類型


C#是一門靜態類型的語言,但是在C#4.0時微軟引入了動態類型的概念。

dynamic

關鍵字dynamic用來定義動態對象,我們來看一下動態類型的一些特性。

調用不同類的相同方法

我們有兩個或多個不相關的類,然后運行時需要可以調用到相同名稱的方法,如下:

 1 using System;
 2 
 3 namespace Study
 4 {
 5     class Program
 6     {
 7         static void Main(string[] args)
 8         {
 9             dynamic obj = GetObject(0);
10             Console.WriteLine(obj.Talk());
11 
12             Console.Read();
13         }
14 
15         private static Object GetObject(int type)
16         {
17             switch (type)
18             {
19                 case 1:
20                     return new Dog();
21             }
22             return new Robot();
23         }
24     }
25 
26     public class Dog
27     {
28         public string Talk()
29         {
30             return "Wang Wang!";
31         }
32     }
33 
34     public class Robot
35     {
36         public string Talk()
37         {
38             return "I`m a Robot!";
39         }
40     }
41 }

我們的兩個類沒有繼承也沒有應用相同的接口,但是可以調用到相同的方法,使用GetObject(1)可以得到想要的結果。

這就是動態類型,在編譯時不會對方法等進行判斷,而是在運行時才進行處理,如果調用到不存在的方法才會報錯。

C#編譯器允許你通過dynamic對象調用任何方法,即使這個方法根本不存在,編譯器也不會在編譯的時候報編譯錯誤。只有在運行的時候,它才會檢查這個對象的實際類型,並檢查在它上面Talk()是什么意思。動態類型將使得C#可以以更加統一而便利的形式表示下列對象:

  • 來自動態編程語言——如Python或Ruby——的對象;
  • 通過IDispatch訪問的COM對象;
  • 通過反射訪問的一般.NET類型;
  • 結構發生過變化的對象——如HTML DOM對象;

當我們得到一個動態類型的對象時,不管它是來自COM還是IronPython、HTML DOM還是反射,只需要對其進行操作即可,動態語言運行時(DLR)會幫我們指出針對特定的對象以及這些操作的具體意義。這將給我們的開發帶來極大的靈活性,並且能夠極大程度上地精簡我們的代碼。

動態類型使用注意

  1. 不能調用擴展方法;
  2. 委托與動態類型不能進行隱式轉換;
  3. 不能調用構造函數和靜態方法;
  4. 類不能繼承dynamic、泛型參數不能使用dynamic和接口實現也不能使用dynamic;

實現動態行為

實現動態行為有3種方法,分別可以用在不同的場合。

使用ExpandoObject類

直接使用ExpandoObject類來實現動態行為,代碼如下:

 1 using System;
 2 using System.Dynamic;
 3 
 4 namespace Study
 5 {
 6     class Program
 7     {
 8         static void Main(string[] args)
 9         {
10             dynamic obj = new ExpandoObject();
11             //添加屬性
12             obj.name = "Li Lei";
13             obj.age = 20;
14             //添加方法
15             obj.Add = (Func<int, int, int>) ((a, b) => a + b);
16 
17             Console.WriteLine("Name: " + obj.name);
18             Console.WriteLine("Age: " + obj.age);
19             Console.WriteLine("Add: " + obj.Add(100, 123));
20 
21             Console.Read();
22         }
23     }
24 }

輸出如下:

1 Name: Li Lei
2 Age: 20
3 Add: 223

繼承DynamicObject類

通過繼承DynamicObject類也可以實現動態效果,示例如下:

 1 using System;
 2 using System.Dynamic;
 3 
 4 namespace Study
 5 {
 6     class Program
 7     {
 8         static void Main(string[] args)
 9         {
10             dynamic obj = new MyClass();
11             obj.name = "Li Lei";
12             obj.age = 20;
13             obj.CallFunc();
14 
15             Console.Read();
16         }
17     }
18 
19     public class MyClass : DynamicObject
20     {
21         public override bool TrySetMember(SetMemberBinder binder, object value)
22         {
23             Console.WriteLine("設置" + binder.Name + "" + value);
24             return true;
25         }
26 
27         public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
28         {
29             Console.WriteLine("調用" + binder.Name + "方法");
30             result = null;
31             return true;
32         }
33     }
34 }

輸出如下:

1 設置name為Li Lei
2 設置age為20
3 調用CallFunc方法

實現IDynamicMetaObjectProvider接口

如果已經繼承了其它的類,則可以通過實現IDynamicMetaObjectProvider接口來實現動態行為,例子如下:

 1 using System;
 2 using System.Dynamic;
 3 using System.Linq.Expressions;
 4 
 5 namespace Study
 6 {
 7     class Program
 8     {
 9         static void Main(string[] args)
10         {
11             dynamic obj = new MyClass();
12             obj.CallFunc();
13 
14             Console.Read();
15         }
16     }
17 
18     public class MyClass : IDynamicMetaObjectProvider
19     {
20         public DynamicMetaObject GetMetaObject(Expression parameter)
21         {
22             Console.WriteLine("獲取元數據");
23             return new MetaDynamic(parameter, this);
24         }
25     }
26 
27     public class MetaDynamic : DynamicMetaObject
28     {
29         public MetaDynamic(Expression expression, object value) : base(expression, BindingRestrictions.Empty, value)
30         {
31         }
32 
33         public override DynamicMetaObject BindInvokeMember(InvokeMemberBinder binder, DynamicMetaObject[] args)
34         {
35             MyClass target = base.Value as MyClass;
36             Expression self = Expression.Convert(base.Expression, typeof (MyClass));
37             var restrictions = BindingRestrictions.GetInstanceRestriction(self, target);
38             Console.WriteLine("調用" + binder.Name + "方法");
39             return new DynamicMetaObject(self, restrictions);
40         }
41     }
42 }

輸出如下:

1 獲取元數據
2 調用CallFunc方法

 


免責聲明!

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



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