C#4.0和VS2010新特性(三)
VS2010之所以那么強大,究其原因是其背后有著強大的C#4.0作為后臺支撐。和以往的所有版本相比,C#4.0的動態性大大增強——dynamic就是一個非常明顯的例子:
?
(一)dynamic初探:?
??????????? 以前因為某些特殊原因,需要動態的調用外部類(假設這個類是實現了某個帶有參數的接口函數的),通常我們只能用反射了。示例代碼如下:
Assembly asm = Assembly.LoadFile(“xxxxx”)
?????? asm.CreateInstance("MyAssembly.ClassName").GetType().InvokeMember("Say", BindingFlags.InvokeMethod, null, asm.CreateInstance("MyAssembly.ClassName "), new string[] { "aaa" });
?
這里順便簡略說一下反射流程:首先通過絕對路徑加載某個NET的dll文件,然后創建該assembly中某個class的instance(該class必須有無參構造函數),獲取其類型之后動態調用其函數Say,“BindingFlags.InvokeMethod”表明是一個普通類方法,“null”的地方是傳遞一個參數名的,和指明最后的string[]中的一串values內容一一匹配的……可見使用反射調用函數是很痛苦的一件事情。
現在呢?您根本不需要那么麻煩了!因為C#的dynamic會為您做好一切的,下面就是見證奇跡的時刻——
Assembly asm = Assembly.LoadFile("xxxxx");
dynamic dfun = asm.CreateInstance("MyAssembly.ClassName");
dfun.Say("Hello!");
?
??????????? 注意到咖啡色的代碼了么——什么?dynamic竟然可以智能感知出動態加載的那個類的方法Say?其實不然:當你按下這個點的時候,IDE是沒有智能感知的,但是如果你知道這個類是有這個方法(因為接口給了其一個契約,必須實現接口中的方法;而接口的方法是公開的),你就可以完全不理會智能感知,照樣寫,照樣編譯通過運行。神奇吧!
??????????? 看到這里,你就不會認為dynamic和var是“差不多”的概念了(var無非是根據賦值的類型編譯器自己判斷;且var不能作為函數返回值類型,但是dynamic可以)。
??????????? 或許有人會疑問:dynamic可以完全替代類似像簡單工廠、抽象工廠一類的東西了咯?我的理解是——不對!從上面的定義中可以得知:dynamic必須首先獲取對象實例,然后動態反射是它做的事情;如果完全取代反射,實例也獲取不到,如何反射呢?真是“巧婦難為無米之炊”啊!
??????????? 說道dynamic可以作為返回值,下面給出一個例子:
class DynamicClass
??? {
??????? public int Num1 { get; set; }
??????? public int Num2 { get; set; }
?
??????? public DynamicClass(int n1, int n2)
??????? {
??????????? Num1 = n1;
??????????? Num2 = n2;
??????? }
?
??????? public dynamic DynamicAction
??????? { get; set; }
??? }
?
??????????? 主函數注意咖啡色部分:
??????????? static void Main(string[] args)
??????? {
??????????? DynamicClass t = new DynamicClass(1, 2);
??????????? t.DynamicAction = new Func<int, int, double>((x, y) => x + y);
??????????? Console.WriteLine(t.DynamicAction.Invoke(t.Num1,t.Num2));
??????? }
?
??????????? 道理很簡單:因為dynamic類型可以賦值任何東西(包括匿名委托),所以我創建了一個匿名委托給它。然后調用計算結果(匿名委托的調用使用Invoke,可以省略)。
??????????? 但是……dynamic不僅僅可以動態反射類方法和屬性,還可以“空中樓閣”般動態地去創建一個類方法和屬性,并且賦值,相信嗎?這是第二話。
?
(二)神奇的ExpandoObject類和自定義動態類擴展:?
dynamic在第一話中已經展示它動態根據賦值類型直接自動完成反射的強大功能?,F在又是一個新奇跡的誕生——
static void Main(string[] args)
??????? {
??????????? dynamic d = new ExpandoObject();
??????????? d.Name = "ServiceBoy";
??????????? d.Action = Func<string>(()=>d.Name;);
??????????? Console.WriteLine(d.Action());
??????? }
??????????? 初看這個代碼只是簡單的讀寫Name屬性,毫無稀奇可言。但是你注意哦——你到MSDN——或者你索性new ExpandoObject().Name 試試看,有Name和Action這個屬性嗎?——沒有啊,真的沒有!嘿,奇了怪了,既然沒有,為什么你可以憑空“捏造出一個屬性”,而且可以給屬性賦值,并且讀取屬性內容呢?
??????????? 俗話說的好——天下沒有白給的食——微軟這個類意在向我們揭露一個驚天的大秘密,那就是你可以自定義dynamic類,讓這個類跟隨你的要求動態的改變自己(比如增加一個新屬性等)。我們可以參照MSDN,給出一個自定義的ExpandoObject:
public class SimpleDynamic : DynamicObject
??? {
??????? Dictionary<string, object> Properties = new Dictionary<string, object>();
??????? Dictionary<string, object[]> Methods = new Dictionary<string, object[]>();
??????? public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object ?result)
??????? {
??????????? if (!Methods.Keys.Contains(binder.Name))
??????????? {
??????????????? Methods.Add(binder.Name, null);
??????????? }
?
??????????? if (args != null)
??????????? {
??????????????? Methods[binder.Name] = args;
??????????? }
?
??????????? StringBuilder sbu = new StringBuilder();
?
?????? ?????foreach (var item in args)
??????????? {
??????????????? sbu.Append(item);
??????????? }
?
??????????? result = sbu.ToString();
??????????? return true;
?
??????? }
?
??????? public override bool TrySetMember(SetMemberBinder binder, object value)
??????? {
??????????? if (!Properties.Keys.Contains(binder.Name))
??????????? {
??????????????? Properties.Add(binder.Name, value.ToString());
??????????? }
??????????? return true;
??????? }
?
??????? public override bool TryGetMember(GetMemberBinder binder, out object result)
??????? {
??????????? return Properties.TryGetValue(binder.Name, out result);
??????? }
}
??????? 首先說明這個例子的作用:隨意增加不重復的屬性并賦值(取值),并且讓你隨意創建或者調用(帶參或無參)函數進行輸入(輸出)。
??????? 分析一下這個類的主要特點:
??????? 一般地,任何一個類——如果需要動態為自身添加屬性、方法等的,就必須實現IDynamicObjectProvidor接口或者是DynamicObject虛類(之所以用虛類的原因是“各取所需”的緣故,DynamicObject類都通過虛方法virtual去“實現”了接口中所有的方法,只要繼承了這個類,讀者可以根據需要“任意”動態覆蓋你要的方法)。這里介紹三個最常見的方法:
- 如果需要支持動態創建寫屬性,必須覆蓋TrySetMember,其方法介紹如下:
| 參數名稱 | 作用說明 |
| binder:SetMemberBinder類型 | 用于獲取動態創建賦值屬性的時候“屬性名”等一些常見信息(如示例中Name獲取動態賦值的那個屬性)。 |
| value:object類型 | 用于獲取設置動態屬性的那個值。 |
- 如果需要支持動態創建讀屬性,必須覆蓋TryGetMember,其參數作用和TrySetMember大致相當,只是反作用(用于獲取某個已有屬性的內容,并且反向傳遞給object作為輸出結果,注意TryGetMember的value是一個out類型)。同時,這個函數多出一個result類型,用于返回已有屬性的存儲的值(NULL拋出異常,被認為是錯誤的)。
?
- 如果需要動態調用函數并輸出結果,必須覆蓋TryInvokeMember方法,此函數比較復雜:
?
| 參數名稱 | 作用說明 |
| binder:InvokeMemberBinder類型 | 用于獲取動態創建函數時候一些與函數相關的屬性: (比如Name是函數名,其中還有一個CallInfo內嵌屬性,您還可以獲得ArgumentNames(C#4.0中最新的可選參數的名稱,通過其ArgumentNameCount獲取可選參數名的總個數))。 ? |
| Args:object[]類型 | 獲取動態給函數賦的值。 |
| result:object類型 | 返回動態函數執行的結果,Null表示異常。 |
?
根據以上表格,對照不難讀懂我的示例代碼——現在假設你是這樣調用的:
1)
dynamic d = new SimpleDynamic();
d.Name = “Serviceboy”;
Console.WriteLine(d.Name);
首先創建了一個d的動態類型,然后當賦值給Name的時候,因為Name是屬性,所以觸發了“TrySetMember”函數,該函數自動檢查是否已經存在這個屬性名,如果不存在,則將其添加進入一個Dictionary中并將對應賦予的值傳遞進去保存起來。當使用輸出的時候,同樣地,TryGetMember被觸發,系統檢測是否預先創建過這個值,如果沒有,則拋出異常;存在的話,取出對應的存儲value并返回給系統。
2)
dynamic d = new SimpleDynamic();
Console.WriteLine(d.Say(“Hello!”));
??????????? 首先創建了一個d的動態類型,當動態創建一個方法的時候,系統檢測是否包含這個方法名,不包含將添加這個方法名到Dictionary保存,接著檢查參數是否為空,不為空把參數賦值給那個函數名作為Key的Dictionary中保存,最后使用StringBuilder串起來賦值給result作為輸出。
下面給出一個比較復雜的例子——自定義的XML創建器(仿Jeffery Zhao):
public class XmlCreator : DynamicObject
??? {
??????? public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
??????? {
?
??????????? //如果除了第一個節點是字符串,后面都是XElement對象,表明此節點是父節點
??????????? if (args[1] is XElement)
??????????? {
??????????????? XElement root = new XElement(args[0].ToString());
??????????????? //把子節點添加到父節點
??????????????? for (int i = 1; i < args.Length; ++i)
??????????????? {
??????????????????? root.Add(args[i]);
??????????????? }
??????????????? result = root;
???????????????
??????????? }
??????????????? //否則是子節點
??????????? else
??????????? {
??????????????? //拷貝所有屬性到數組:
??????????????? string[] attributes = new string[binder.CallInfo.ArgumentNames.Count];
??????????????? for (int i = 0; i < binder.CallInfo.ArgumentNames.Count; i++)
??????????????? {
??????????????????? attributes[i] = binder.CallInfo.ArgumentNames[i];
??????????????? }
?
??????????????? //拷貝所有屬性值到數組:
??????????????? string[] values = new string[args.Length - 1];
??????????????? for (int i = 1; i < args.Length; ++i)
??????????????? {
??????????????????? values[i - 1] = args[i].ToString();
??????????????? }
?
??????????????? XElement subelement = new XElement(args[0].ToString());
?
??????????????? //屬性名稱獲取
?????????? ?????for (int i = 0; i < attributes.Length; ++i)
??????????????? {
??????????????????? subelement.SetAttributeValue(attributes[i], values[i]);
??????????????? }
??????????????? result = subelement;
??????????? }
??????????? return result != null;
??????? }
}
該函數功能是:輸出任意同時帶有屬性的節點,同時可以嵌套——比如:
dynamic xmlCreator = new ?XmlCreator();
XElement ele = xmlCreator.CreateElement(“Books”,
xmlCreator(“Book”,name:C#,price:100.50)
??????????????????????? );
?
??????????? 大家可以自己想一想是怎么一個原理哦。
轉載于:https://www.cnblogs.com/serviceboy/archive/2010/04/16/1713688.html
總結
以上是生活随笔為你收集整理的C#4.0和VS2010新特性(三)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [CRM] CRM 产品周期
- 下一篇: SQL Cookbook:一、检索记录(