進階系列(10)—— C#元數據和動態編程

1、元數據的介紹

    元數據是用來描述數據的數據(Data that describes other data)。單單這樣說,不太好理解,我來舉個例子。下面是契訶夫的小說《套中人》中的一段,描寫一個叫作瓦蓮卡的女子:python

(她)年紀已經不輕,三十歲上下,個子高挑,身材勻稱,黑黑的眉毛,紅紅的臉蛋--一句話,不是姑娘,而是果凍,她那樣活躍,吵吵嚷嚷,不停地哼着小俄羅斯的抒情歌曲,高聲大笑,動不動就發出一連串響亮的笑聲:哈,哈,哈!

    這段話裏提供了這樣幾個信息:年齡(三十歲上下)、身高(個子高挑)、相貌(身材勻稱,黑黑的眉毛,紅紅的臉蛋)、性格(活躍,吵吵嚷嚷,不停地哼着小俄羅斯的抒情歌曲,高聲大笑)。有了這些信息,咱們就能夠大體想像出瓦蓮卡是個什麼樣的人。推而廣之,只要提供這幾類的信息,咱們也能夠推測出其餘人的樣子。這個例子中的"年齡"、"身高"、"相貌"、"性格",就是元數據,由於它們是用來描述具體數據/信息的數據/信息。git

   固然,這幾個元數據用來刻畫我的情況還不夠精確。咱們每一個人從小到大,都填過《我的狀況登記表》之類的東西吧,其中包括姓名、性別、民族、政治面貌、一寸照片、學歷、職稱等等......這一套元數據纔算比較完備。程序員

   在平常生活中,元數據無所不在。有一類事物,就能夠定義一套元數據。喜歡拍攝數碼照片的朋友應該知道,每張數碼照片都包含EXIF信息。它就是一種用來描述數碼圖片的元數據。按照Exif 2.1標準,其中主要包含這樣一些信息:數據庫

Image Description 圖像描述、來源. 指生成圖像的工具
Artist 做者 有些相機能夠輸入使用者的名字
Make 生產者 指產品生產廠家
Model 型號 指設備型號
Orientation方向 有的相機支持,有的不支持
XResolution/YResolution X/Y方向分辨率 本欄目已有專門條目解釋此問題。
ResolutionUnit分辨率單位 通常爲PPI
Software軟件 顯示固件Firmware版本
DateTime日期和時間
YCbCrPositioning 色相定位
ExifOffsetExif信息位置,定義Exif在信息在文件中的寫入,有些軟件不顯示。
ExposureTime 曝光時間 即快門速度
FNumber光圈係數
ExposureProgram曝光程序 指程序式自動曝光的設置,各相機不一樣,多是Sutter Priority(快門優先)、Aperture Priority(快門優先)等等。
ISO speed ratings感光度
ExifVersionExif版本
DateTimeOriginal建立時間
DateTimeDigitized數字化時間
ComponentsConfiguration圖像構造(多指色彩組合方案)
CompressedBitsPerPixel(BPP)壓縮時每像素色彩位 指壓縮程度
ExposureBiasValue曝光補償。
MaxApertureValue最大光圈
MeteringMode測光方式, 平均式測光、中央重點測光、點測光等。
Lightsource光源 指白平衡設置
Flash是否使用閃光燈。
FocalLength焦距,通常顯示鏡頭物理焦距,有些軟件能夠定義一個係數,從而顯示至關於35mm相機的焦距 MakerNote(User Comment)做者標記、說明、記錄
FlashPixVersionFlashPix版本 (個別機型支持)
ColorSpace色域、色彩空間
ExifImageWidth(Pixel X Dimension)圖像寬度 指橫向像素數
ExifImageLength(Pixel Y Dimension)圖像高度 指縱向像素數
Interoperability IFD通用性擴展項定義指針 和TIFF文件相關,具體含義不詳
FileSource源文件 Compression壓縮比。

我再舉一個例子。在電影數據庫IMDB上能夠查到每一部電影的信息。IMDB自己也定義了一套元數據,用來描述每一部電影。下面是它的一級元數據,每一級下面又列出了二級元數據,總共加起來,能夠從100多個方面刻畫一部電影:express

Cast and Crew(演職人員)、Company Credits(相關公司)、Basic Data(基本狀況)、Plot & Quotes(情節和引語)、Fun Stuff(趣味信息)、Links to Other Sites(外部連接)、Box Office and Business(票房和商業開發)、Technical Info(技術信息)、Literature(書面內容)、Other Data(其餘信息)。

元數據最大的好處是,它使信息的描述和分類能夠實現格式化,從而爲機器處理創造了可能。緩存

2、元數據的使用

      你是 Adventure Works 公司的開發人員,當前你須要建立一個應用程序,以報告由該公司開發人員編寫的代碼。該應用程序將加載一個程序集並使用反射來生成要用於代碼評審和文檔化的報告。要改進性能,須要將該應用程序建立爲控制檯應用程序。在本練習中,將使用 Assembly 類加載程序集,而程序集名稱和路徑應由用戶提供。
     你但願建立一個對不一樣應用程序中編寫的代碼進行文檔化的應用程序。在本練習中,該應用程序將使用多個信息類型反射類,以生成一個用於代碼評審和文檔化的基於控制檯的報告。該報告會列舉程序集的名稱、位置、版本和其餘程序集級別元數據。該報告還會列舉程序集中的模塊、類、成員、方法和屬性。
ide

(一)步驟1:顯示程序集信息函數

   1)   聲明一個接受程序集做爲參數的名爲GenerateReport 的方法。工具

   2)   編寫代碼,以顯示程序集信息。根據程序集在全局程序集緩存中的位置,顯示程序集的名稱和位置以及程序集的狀態。性能

         結果:已經顯示程序集信息。

(二)步驟2:顯示程序集中全部模塊和類型

  1)  使用 GetModules 方法建立一個包含程序集中模塊的報告。該代碼會爲程序集中的每一個模塊調用DisplayModuleInfo方法。

  2)  聲明一個接受Module 方法做爲參數的名爲 DisplayModuleInfo 的方法。

  3)  經過使用 GetTypes 方法來顯示每一個方法中包含的類型列表。該代碼會爲模塊中的每一個類型調用 DisplayTypeInfo 方法。

  結果:已經顯示了該程序集包含的模塊列表和每一個模塊包含的類型列表。

(三)步驟3:顯示全部類型中的字段、屬性和方法。

   1)  聲明一個接受Type 做爲參數的名爲 DisplayTypeInfo 的方法。

   2)  使用 GetFields 方法顯示全部類型中的字段列表。

         該列表應包含如下信息:字段名 字段類型 字段是公共仍是私有

   3)   使用 GetProperties 方法顯示全部屬性列表。

        該列表應包含如下信息: 屬性名屬性類型 屬性是可讀仍是可寫

  4)    顯示當前類型中全部方法的列表。

       該代碼會爲類型中的每一個方法調用 DisplayMethodInfo 方法。

       結果:已經顯示每種類型中的字段列表、屬性列表以及方法列表。

(四)步驟4:顯示全部方法中包含的信息

   1)   聲明一個接受 MethodInfo 做爲參數的名爲 DisplayMethodInfo 的方法。

   2)   使用 GetParameters 方法顯示方法頭文件信息。

   3)   使用 GetParameters 方法顯示全部參數的列表。

         該列表應包含如下信息:參數名參數類型參數是否爲 IsOptional、IsOut 或 IsRetval。

   4)  使用 GetMethodBody 方法顯示關於每一個方法的信息。

        該信息應該包含方法中定義的堆棧大小和局部變量詳細信息,例如LocalIndex和LocalType。

   5)  編譯並運行該應用程序。
       主要代碼以下:

  using System;
  using System.Collections.Generic;
  using System.Text;
  using System.Reflection;
  
  namespace crseae_lab
  {
      class Program
      {
         static string ReadAssemblyFullPath()
         {
             string AssemblyPath, AssemblyName;
             Console.Write("Type the assembly path: ");
             AssemblyPath = Console.ReadLine();
             Console.Write("Type the assembly name: ");
             AssemblyName = Console.ReadLine();
             return AssemblyPath + @"/" + AssemblyName;
         }
        
         static void Main(string[] args)
         {
             string AssemblyFullPath;
             AssemblyFullPath = ReadAssemblyFullPath();
             GenerateReport(AssemblyFullPath);
             Console.Read();
         }
         static void GenerateReport(string path)
         {
             Assembly myAssemble = Assembly.LoadFile(path);
             Console.WriteLine(myAssemble.FullName);

            //myAssemble.
             Console.WriteLine(string.Empty.PadLeft(, '-'));
             foreach (Type type in myAssemble.GetTypes())
             {
                 Console.WriteLine("The name of Assemble is:"+type.Name);
                 Console.WriteLine("The path is:" + type.FullName);
             }
             GetModules(myAssemble);
         }
         static void GetModules(Assembly name)
         {
             Console.WriteLine(string.Empty.PadLeft(, '-'));
             foreach (Module module in name.GetModules())
             {
                 Console.WriteLine("Module name:" + module.Name);
                 DisplayModuleInfo(module);
             }
         }
         static void DisplayModuleInfo(Module module)
         {
             Console.WriteLine(string.Empty.PadLeft(, '-'));
             foreach (Type type in module.GetTypes())
             {
                 Console.WriteLine("Module type name is:" + type.Name);
                 DisplayTypeInfo(type);
             }
         }
         static void DisplayTypeInfo(Type type)
         {
             foreach (FieldInfo fieldInfo in type.GetFields())
             {
                 Console.WriteLine("The name of this field is:" + fieldInfo.Name);
                 Console.WriteLine("The type of this field is:" + fieldInfo.GetType().ToString());
                 if (fieldInfo.IsPrivate)

                {
                     Console.WriteLine("This field is Private!");
                 }
                 else
                 {
                     Console.WriteLine("This field is public");
                 }
             }
             Console.WriteLine(string.Empty.PadLeft(, '-'));
             foreach (PropertyInfo propertyInfo in type.GetProperties())
             {
                 Console.WriteLine("The name of this property is:" + propertyInfo.Name);
                 Console.WriteLine("Type of this property is:" + propertyInfo.PropertyType.ToString());
                 if (propertyInfo.CanWrite)
                 {
                     Console.WriteLine("This property can write!");
                 }
                 else
                 {
                     Console.WriteLine("This property can't be write");
                 }
             }
             Console.WriteLine(string.Empty.PadLeft(, '-'));
             foreach (MethodInfo method in type.GetMethods())
             {
                 Console.WriteLine("Method name:" + method.Name);
                 DisplayMethodInfo(method);
             }
         }
         static void DisplayMethodInfo(MethodInfo method)
         {
             Console.WriteLine(string.Empty.PadLeft(, '-'));
             foreach (ParameterInfo parInfo in method.GetParameters())
             {

                 Console.WriteLine("Parameter name is:"+parInfo.Name);
                Console.WriteLine("Parameter type is:" + parInfo.ParameterType.ToString());
                if (parInfo.IsOptional)
                {
                    Console.WriteLine("This parameter is option!");
                }
                else
                    Console.WriteLine("This parrameter is not option!");
                if (parInfo.IsOut)
                {
                    Console.WriteLine("This parameter is out!");
                }
                else
                    Console.WriteLine("This parameter is not out!");
                if (parInfo.IsRetval)
                    Console.WriteLine("and is retval");
                else
                    Console.WriteLine("and is not retval");
            }
            if (method.GetMethodBody() != null)
            {
                GetMethodBody(method.GetMethodBody());
            }
        }
        static void GetMethodBody(MethodBody body)
        {
            Console.WriteLine(string.Empty.PadLeft(, '-'));
            Console.WriteLine("Body Information :");
            Console.WriteLine("Max stack size:" + body.MaxStackSize);
            Console.WriteLine("Local variables are initialized:" + body.InitLocals);
            Console.WriteLine(string.Empty.PadLeft(, '-'));
            foreach (LocalVariableInfo local in body.LocalVariables)
            {
                Console.WriteLine("Index of local var is:" + local.LocalIndex);

                Console.WriteLine("Type of local var is:"+local.LocalType.ToString());
            }
            Console.WriteLine(string.Empty.PadLeft(, '*'));
        }
    }
}

3、動態類型介紹

  提到動態類型固然就要說下靜態類型了,對於什麼是靜態類型呢? 你們都知道以前C#一直都是靜態語言(指定的是沒有引入動態類型以前,這裏說明下,不是引入了動態類型後C#就是動態語言,只是引入動態類型後,爲C#語言增添了動態語言的特性,C#仍然是靜態語言),之因此稱爲靜態語言,以前咱們寫代碼時,例如 int i =5;這樣的代碼,此時i 咱們已經明確知道它的類型爲int了,然而這樣的代碼,變量的類型的肯定是在編譯時肯定的,對應的,若是類型的肯定是在執行時才肯定的類型,這樣的類型就是動態類型(C# 4.0中新添加了一個dynamic 關鍵字來定義咱們的動態類型)。面對動態類型,C#編譯器作的工做只是完成檢查語法是否正確,但沒法肯定所調用的方法或屬性是否正確(之因此會這樣,主要仍是由於動態類型是運行時才知道它的具體類型,因此編譯器編譯的時候確定不知道類型,就沒辦法判斷調用的方法或屬性是否是存在和正確了,因此對於動態類型,將不能使用VS提供的智能提示的功能,這樣寫動態類型代碼時就要求開發人員對於某個動態類型必須準確知道其類型後和所具備的方法和屬性了,不能這些錯誤只能在運行程序的過程拋出異常的方式被程序員所發現。)

補充: 講到dynamic關鍵字,也許你們會想到C# 3中的var關鍵字,這裏這裏補充說明下dynamic, var區別。var 關鍵字不過是一個指令,它告訴編譯器根據變量的初始化表達式來推斷類型。(記住var並非類型),而C# 4中引入的dynamic是類型,可是編譯時不屬於CLR類型(指的int,string,bool,double等類型,運行時確定CLR類型中一種的),它是包含了System.Dynamic.DynamicAttribute特性的System.Object類型,但與object又不同,不同主要體如今動態類型不會在編譯時時執行顯式轉換,下面給出一段代碼代碼你們就會很容易看出區別了:

  object obj = 10;
            Console.WriteLine(obj.GetType());
            // 使用object類型此時須要強制類型轉換,不能編譯器會出現編譯錯誤
            obj = (int)obj + 10;

            dynamic dynamicnum = 10;
            Console.WriteLine(dynamicnum.GetType());
            // 對於動態類型而言,編譯時編譯器根本不知道它是什麼類型,
            // 因此編譯器就判斷不了dynamicnum的類型了,因此下面的代碼不會出現編譯時錯誤
            // 由於dynamicnum有多是int類型,編譯器不知道該變量的具體類型不能憑空推測類型
            // 固然也就不能提示咱們編譯時錯誤了
            dynamicnum = dynamicnum + 10;

4、爲何須要動態類型

  第一部分和你們介紹了什麼是動態類型,對於動態類型,總結爲一句話爲——運行時肯定的類型。然而你們瞭解了動態類型究竟是什麼以後,固然又會出現新的問題了,即動態類型有什麼用的呢? C# 爲何好端端的引入動態類型增長程序員的負擔呢? 事實並非這樣的,下面就介紹了動態類型到底有什麼用,它並非所謂給程序員帶來負擔,必定程度上講是福音

 (一)使用動態類型能夠減小強制類型轉換

  從第一部分的補充也能夠看到,使用動態類型不須要類型轉換是由於編譯器根本在編譯時的過程知道什麼類型,既然不知道是什麼類型,怎麼判斷該類型是否能進行什麼操做,因此也就不會出現相似「運算符「+」沒法應用於「object」和「int」類型的操做數「或者」不存在int類型到某某類型的隱式轉換「的編譯時錯誤了,可能這點用戶,開發人員可能並不以爲多好的,由於動態類型沒有智能提示的功能。 可是動態類型減小了強制類型轉換的代碼以後,可讀性仍是會有所加強。(這裏又涉及到我的取捨問題的, 若是本身以爲那種方式方便就用那種的,不必必定要用動態類型,主要是看那種方式可讓本身和其餘開發人員更好理解)

(二)使用動態類型可使C#靜態語言中調用Python等動態語言

  對於這點,可能朋友有個疑問,爲何要在C#中使用Python這樣的動態語言呢? 對於這個疑問,就和在C#中經過P/Invoke與本地代碼交互,以及與COM互操做的道理同樣,假設咱們要實現的功能在C#類庫中沒有,然而在Python中存在時,此時咱們就能夠直接調用Python中存在的功能了。

5、動態類型的使用

前面兩部分和你們介紹動態類型的一些基礎知識的,瞭解完基礎知識以後,你們確定很火燒眉毛地想知道如何使用動態類型的,下面給出兩個例子來演示動態類型的使用的。

(一)C# 4 經過dynamic關鍵字來實現動態類型

 dynamic dyn = 5;
            Console.WriteLine(dyn.GetType());
            dyn = "test string";
            Console.WriteLine(dyn.GetType());
            dynamic startIndex = 2;
            string substring = dyn.Substring(startIndex);
            Console.WriteLine(substring);
            Console.Read();

 運行結果爲:

(二)在C#中調用Python動態語言(要運行下面的代碼,必須下載並安裝IronPython,IronPython 是在 .NET Framework 上實現的第一種動態語言。http://ironpython.codeplex.com下載 )

  // 引入動態類型以後
        // 能夠在C#語言中與動態語言進行交互
        // 下面演示在C#中使用動態語言Python
            ScriptEngine engine = Python.CreateEngine();
            Console.Write("調用Python語言的print函數輸出: ");
            // 調用Python語言的print函數來輸出
            engine.Execute("print 'Hello world'");
            Console.Read();

運行結果:

6、動態類型背後的故事

 知道了如何在C#中調用動態語言以後,然而爲何C# 爲何可使用動態類型呢?C#編譯器到底在背後爲咱們動態類型作了些什麼事情的呢? 對於這些問題,答案就是DLR(Dynamic Language Runtime,動態語言運行時),DLR使得C#中能夠調用動態語言以及使用dynamic的動態類型。提到DLR時,可能你們會想到.Net Framework中的CLR(公共語言運行時),然而DLR 與CLR究竟是什麼關係呢?下面就看看.Net 4中的組件結構圖,相信你們看完以後就會明白二者之間的區別:

 

從圖中能夠看出,DLR是創建在CLR的基礎之上的,其實動態語言運行時是動態語言和C#編譯器用來動態執行代碼的庫,它不具備JIT編譯,垃圾回收等功能。然而DLR在代碼的執行過程當中扮演的是什麼樣的角色呢? DLR所扮演的角色就是——DLR經過它的綁定器(binder)和調用點(callsite),元對象來把代碼轉換爲表達式樹,而後再把表達式樹編譯爲IL代碼,最後由CLR編譯爲本地代碼(DLR就是幫助C#編譯器來識別動態類型)。 這裏DLR扮演的角色並非憑空想象出來的,並且查看它的反編譯代碼來推出來的,下面就具體給出一個例子來講明DLR背後所作的事情。C#源代碼以下:

 class Program
    {
        static void Main(string[] args)
        {
            dynamic text = "test text";
            int startIndex = 2;
            string substring = text.Substring(startIndex); 
            Console.Read();
        }
    }

經過Reflector工具查看生成的IL代碼以下:

private static void Main(string[] args)
{
    object text = "test text";
    int startIndex = 2;
    if (<Main>o__SiteContainer0.<>p__Site1 == null)
    {
        // 建立用於將dynamic類型隱式轉換爲字符串的調用點
        <Main>o__SiteContainer0.<>p__Site1 = CallSite<Func<CallSite, object, string>>.Create(Binder.Convert(CSharpBinderFlags.None, typeof(string), typeof(Program)));
    }
    if (<Main>o__SiteContainer0.<>p__Site2 == null)
    {
        // 建立用於調用Substring函數的調用點
        <Main>o__SiteContainer0.<>p__Site2 = CallSite<Func<CallSite, object, int, object>>.Create(Binder.InvokeMember(CSharpBinderFlags.None, "Substring", null, typeof(Program), new CSharpArgumentInfo[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null) }));
    }
    
    // 調用調用點,首先調用<>p_Site2,即Substring方法,再調用<>P_Site1來將結果進行轉換
    string substring = <Main>o__SiteContainer0.<>p__Site1.Target(<Main>o__SiteContainer0.<>p__Site1, <Main>o__SiteContainer0.<>p__Site2.Target(<Main>o__SiteContainer0.<>p__Site2, text, startIndex));
    Console.Read();
}

//編譯器生成的內嵌類型爲
[CompilerGenerated]
private static class <Main>o__SiteContainer0
{
    // Fields
    public static CallSite<Func<CallSite, object, string>> <>p__Site1;
    public static CallSite<Func<CallSite, object, int, object>> <>p__Site2;
}

從IL代碼中能夠看出Main方法內包含兩個動態操做,由於編譯器生成的內嵌類型包含兩個調用點(CallSite<T>,CallSite<T>便是System.Runtime.CompilerServices命名空間下的一個類,關於CallSite的具體信息能夠查看MSDN中的介紹——CallSite<T> )字段,一個是調用Substring方法(即<>p__Site2),一個是將結果(編譯時時dynamic)動態地轉換爲字符串(即<>p__Site1),下面給出動態類型的執行過程(注意DLR中有一個緩存的概念):

 

7、動態類型的約束

相信經過前面幾部分的介紹你們已經對動態類型有了必定的瞭解的,尤爲是第四部分的介紹以後,你們應該對於動態類型的執行過程也有了一個清晰的認識了,然而有些函數時不能經過動態綁定來進行調用的,這裏就涉及到類型類型的約束:

(一)不能用動態類型做爲參數調用擴展方法

不能用動態類型做爲參數來調用擴展方法的緣由是——調用點知道編譯器所知道的靜態類型,可是它不知道調用所在的源文件在哪裏,以及using指令引入了哪些命名空間,因此在編譯時調用點就找不到哪些擴展方法可使用,因此就會出現編譯時錯誤。下面給出一個簡單的示例程序:

var numbers = Enumerable.Range(10, 10);
            dynamic number = 4;
            var error = numbers.Take(number);  // 編譯時錯誤

            // 經過下面的方式來解決這個問題
            // 1. 將動態類型轉換爲正確的類型
            var right1 = numbers.Take((int)number);
            // 2. 用調用靜態方法的方式來進行調用
            var right2 = Enumerable.Take(numbers, number);

(二) 委託與動態類型不能隱式轉換的限制

若是須要將Lambda表達式,匿名方法轉化爲動態類型時,此時編譯器必須知道委託的確切類型,不能不增強制轉化就把他們設置爲Delegae或object變量,此時不一樣string,int類型(由於前面int,string類型能夠隱式轉化爲動態類型,編譯器此時會把他們設置爲object類型。可是匿名方法和Lambda表達式不能隱式轉化爲動態類型),若是須要完成這樣的轉換,此時必須強制指定委託的類型,下面是一個演示例子:

dynamic lambdarestrict = x => x + 1; // 編譯時錯誤
            // 解決方案
            dynamic rightlambda =(Func<int,int>)( x=>x+1);

            dynamic methodrestrict = Console.WriteLine; // 編譯時錯誤
            // 解決方案
            dynamic rightmethod =(Action<string>)Console.WriteLine;

(三) 動態類型不能調用構造函數和靜態方法的限制——即不能對動態類型調用構造函數或靜態方法,由於此時編譯器沒法指定具體的類型。

 dynamic s = new dynamic();

(四) 類型聲明和泛型類型參數

不能聲明一個基類爲dynamic的類型,也不能將dynamic用於類型參數的約束,或做爲類型所實現的接口的一部分,下面看一些具體的例子來加深概念的理解:

// 基類不能爲dynamic 類型
    class DynamicBaseType : dynamic
    {
    }
    // dynamic類型不能爲類型參數的約束
    class DynamicTypeConstrain<T> where T : dynamic
    { 
    }
    // 不能做爲所實現接口的一部分
    class DynamicInterface : IEnumerable<dynamic>
    {
    }

8、實現動態的行爲

 介紹了這麼動態類型,是否是你們都火燒眉毛地想知道若是讓本身的類型具備動態的行爲呢? 然而實現動態行爲有三種方式:

  • 使用ExpandObject
  • 使用DynamicObject
  • 實現IDynamicMetaObjectProvider接口.

下面就從最簡單的方式:

(一)使用ExpandObject來實現動態的行爲

using System;
// 引入額外的命名空間
using System.Dynamic;

namespace 自定義動態類型
{
    class Program
    {
        static void Main(string[] args)
        {
            dynamic expand = new ExpandoObject();
            // 動態爲expand類型綁定屬性
            expand.Name = "Learning Hard";
            expand.Age = 24;

            // 動態爲expand類型綁定方法
            expand.Addmethod = (Func<int, int>)(x => x + 1);
            // 訪問expand類型的屬性和方法
            Console.WriteLine("expand類型的姓名爲:"+expand.Name+" 年齡爲: "+expand.Age);
            Console.WriteLine("調用expand類型的動態綁定的方法:" +expand.Addmethod(5));
            Console.Read();
        }
    }
}

運行的結果和預期的同樣,運行結果爲:

(二)使用DynamicObject來實現動態行爲

static void Main(string[] args)
        {
           dynamic dynamicobj = new DynamicType();
            dynamicobj.CallMethod();
            dynamicobj.Name = "Learning Hard";
            dynamicobj.Age = "24";
            Console.Read();
        }
  class DynamicType : DynamicObject
    {
        // 重寫方法,
        // TryXXX方法表示對對象的動態調用
        public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
        {
            Console.WriteLine(binder.Name +" 方法正在被調用");
            result = null;
            return true;
        }

        public override bool TrySetMember(SetMemberBinder binder, object value)
        {
            Console.WriteLine(binder.Name + " 屬性被設置," + "設置的值爲: " + value);
            return true;
        }
    }

運行結果爲:

(三) 實現IDynamicMetaObjectProvider接口來實現動態行爲

因爲Dynamic類型在運行時來動態建立對象的,因此對該類型的每一個成員的訪問都會調用GetMetaObject方法來得到動態對象,而後經過這個動態對象來進行調用,因此實現IDynamicMetaObjectProvider接口,須要實現一個GetMetaObject方法來返回DynamicMetaObject對象,演示代碼以下:

 static void Main(string[] args)
        {  
            dynamic dynamicobj2 = new DynamicType2();
            dynamicobj2.Call();
            Console.Read();
}
public class DynamicType2 : IDynamicMetaObjectProvider
    {
        public DynamicMetaObject GetMetaObject(Expression parameter)
        {
            Console.WriteLine("開始得到元數據......");
            return new Metadynamic(parameter,this);
        }
    }

    // 自定義Metadynamic類
    public class Metadynamic : DynamicMetaObject
    {
        internal Metadynamic(Expression expression, DynamicType2 value)
            : base(expression, BindingRestrictions.Empty, value)
        {
        }
        // 重寫響應成員調用方法
        public override DynamicMetaObject BindInvokeMember(InvokeMemberBinder binder, DynamicMetaObject[] args)
        {
            // 得到真正的對象
            DynamicType2 target = (DynamicType2)base.Value;
            Expression self = Expression.Convert(base.Expression, typeof(DynamicType2));
            var restrictions = BindingRestrictions.GetInstanceRestriction(self, target);
            // 輸出綁定方法名
            Console.WriteLine(binder.Name + " 方法被調用了");
            return new DynamicMetaObject(self, restrictions);
        }
    }

運行結果爲:

相關文章
相關標籤/搜索