CLR Inside Out - Reflection

您清晰的組件化目標是否因在庫間共享過多類型信息而落空?或許您須要高效的強類型化數據存儲,但若是每次對象模型發展後都須要更新您的數據庫架構,那會耗費很大成本,因此您更願意在運行時推斷出其類型架構嗎?您須要交付能接受任意用戶對象的組件,並以某種智能化的方式處理它們嗎?您但願庫的調方者能以編程方式向您說明它們的類型嗎?
若是您發現本身在苦苦維持強類型化數據結構的同時,又冀望於最大化運行時靈活性,那麼您大概會願意考慮反射,以及它如何改善您的軟件。在本專欄中,我將探討 Microsoft ® .NET Framework 中的 System.Reflection 命名空間,以及它如何爲您的開發體驗提供助益。我將從一些簡單的示例開始,最後將講述如何處理現實世界中的序列化情形。在此過程當中,我會展現反射和 CodeDom 如何配合工做,以有效處理運行時數據。
在深刻探究 System.Reflection 以前,我想先討論一下通常的反射編程。首先,反射可定義爲由一個編程系統提供的任何功能,此功能使程序員能夠在無需提早了解其標識或正式結構的狀況下檢查和操做代碼實體。這部份內容不少,我將逐一展開說明。
首先,反射提供了什麼呢?您能用它作些什麼呢?我傾向於將典型的以反射爲中心的任務分爲兩類:檢查和操做。檢查須要分析對象和類型,以收集有關其定義和行爲的結構化信息。除了一些基本規定以外,一般這是在事先不瞭解它們的狀況下進行的。(例如,在 .NET Framework 中,任何東西都繼承自 System.Object,而且一個對象類型的引用一般是反射的通常起點。)
操做利用經過檢查收集到的信息動態地調用代碼,建立已發現類型的新實例,或者甚至能夠輕鬆地動態從新結構化類型和對象。須要指出的一個要點是,對於大多數系統,在運行時操做類型和對象,較之在源代碼中靜態地進行同等操做,會致使性能下降。因爲反射的動態特性,所以這是個必要的取捨,不過有不少技巧和最佳作法能夠優化反射的性能(有關優化使用反射的更多深刻信息,請參見 msdn.microsoft.com/msdnmag/issues/05/07/Reflection)。
那麼,什麼是反射的目標呢?程序員實際檢查和操做什麼呢?在我對反射的定義中,我用了「代碼實體」這個新術語,以強調一個事實:從程序員的角度來講,反射技術有時會使傳統對象和類型之間的界限變得模糊。例如,一個典型的以反射爲中心的任務多是:
  1. 從對象 O 的句柄開始,並使用反射得到其相關定義(類型 T)的句柄。
  2. 檢查類型 T,得到它的方法 M 的句柄。
  3. 調用另外一個對象 O’(一樣是類型 T)的方法 M。
請注意,我在從一個實例穿梭到它的底層類型,從這一類型到一個方法,以後又使用此方法的句柄在另外一個實例上調用它 — 顯然這是在源代碼中使用傳統的 C# 編程技術沒法實現的。在下文中探討 .NET Framework 的 System.Reflection 以後,我會再次經過一個具體的例子來解釋這一情形。
某些編程語言自己能夠經過語法提供反射,而另外一些平臺和框架(如 .NET Framework)則將其做爲系統庫。無論以何種方式提供反射,在給定情形下使用反射技術的可能性至關複雜。編程系統提供反射的能力取決於諸多因素:程序員很好地利用了編程語言的功能表達了他的概念嗎?編譯器是否在輸出中嵌入足夠的結構化信息(元數據),以方便往後的解讀?有沒有一個運行時子系統或主機解釋器來消化這些元數據?平臺庫是否以對程序員有用的方式,展現此解釋結果?
若是您頭腦中想象的是一個複雜的、面向對象類型的系統,但在代碼中卻表現爲簡單的、C 語言風格的函數,並且沒有正式的數據結構,那麼顯然您的程序不可能動態地推斷出,某變量 v1 的指針指向某種類型 T 的對象實例。由於畢竟類型 T 是您頭腦中的概念,它從未在您的編程語句中明確地出現。但若是您使用一種更爲靈活的面嚮對象語言(如 C#)來表達程序的抽象結構,並直接引入類型 T 的概念,那麼編譯器就會把您的想法轉換成某種往後能夠經過合適的邏輯來理解的形式,就象公共語言運行時 (CLR) 或某種動態語言解釋器所提供的同樣。
反射徹底是動態、運行時的技術嗎?簡單的說,不是這樣。整個開發和執行週期中,不少時候反射對開發人員均可用且有用。一些編程語言經過獨立編譯器實現,這些編譯器將高級代碼直接轉換成機器可以識別的指令。輸出文件只包括編譯過的輸入,而且運行時沒有用於接受不透明對象並動態分析其定義的支持邏輯。這正是許多傳統 C 編譯器的情形。由於在目標可執行文件中幾乎沒有支持邏輯,所以您沒法完成太多動態反射,然而編譯器會不時提供靜態反射 — 例如,廣泛運用的 typeof 運算符容許程序員在編譯時檢查類型標識。
另外一種徹底不一樣的狀況是,解釋性編程語言老是經過主進程得到執行(腳本語言一般屬於此類)。因爲程序的完整定義是可用的(做爲輸入源代碼),並跟完整的語言實現結合在一塊兒(做爲解釋器自己),所以全部支持自我分析所需的技術都到位了。這種動態語言頻繁地提供全面反射功能,以及一組用於動態分析和操做程序的豐富工具。
.NET Framework CLR 和它的承載語言如 C# 屬於中間形態。編譯器用來把源代碼轉換成 IL 和元數據,後者與源代碼相比雖屬於較低級別或者較低「邏輯性」,但仍然保留了不少抽象結構和類型信息。一旦 CLR 啓動和承載了此程序,基類庫 (BCL) 的 System.Reflection 庫即可以使用此信息,並返回關於對象類型、類型成員、成員簽名等的信息。此外,它也能夠支持調用,包括後期綁定調用。

.NET 中的反射
要在用 .NET Framework 編程時利用反射,您可使用 System.Reflection 命名空間。此命名空間提供封裝了不少運行時概念的類,例如程序集、模塊、類型、方法、構造函數、字段和屬性。 圖 1 中的表顯示,System.Reflection 中的類如何與概念上運行時的對應項對應起來。
  Figure 1 System.Reflection 類

語言組件 相應的 .NET 類
程序集 System.Reflection.Assembly
模塊 System.Reflection.Module
抽象成員 System.Reflection.MemberInfo(如下全部的基類)
類型 System.Type
屬性 System.Reflection.PropertyInfo
字段 System.Reflection.FieldInfo
事件 System.Reflection.EventInfo
抽象方法 System.Reflection.MethodBase(如下全部的基類)
方法 System.Reflection.MethodInfo
構造函數 System.Reflection.ConstructorInfo
儘管很重要,不過 System.Reflection.Assembly 和 System.Reflection.Module 主要用於定位新代碼並將其加載到運行時。本專欄中,我暫不討論這些部分,而且假定全部相關代碼都已經加載。
要檢查和操做已加載代碼,典型模式主要是 System.Type。一般,您從得到一個所關注運行時類別的 System.Type 實例開始(經過 Object.GetType)。接着您可使用 System.Type 的各類方法,在 System.Reflection 中探索類型的定義並得到其它類的實例。例如,若是您對某特定方法感興趣,並但願得到此方法的一個 System.Reflection.MethodInfo 實例(可能經過 Type.GetMethod)。一樣,若是您對某字段感興趣,並但願得到此字段的一個 System.Reflection.FieldInfo 實例(可能經過 Type.GetField)。
一旦得到全部必要的反射實例對象,便可根據須要遵循檢查或操做的步驟繼續。檢查時,您在反射類中使用各類描述性屬性,得到您須要的信息(這是通用類型嗎?這是實例方法嗎?)。操做時,您能夠動態地調用並執行方法,經過調用構造函數建立新對象,等等。

檢查類型和成員
讓咱們跳轉到一些代碼中,探索如何運用基本反射進行檢查。我將集中討論類型分析。從一個對象開始,我將檢索它的類型,然後考察幾個有意思的成員(請參見 圖 2)。
  Figure 2 檢索對象類型和成員

代碼
using System;
using System.Reflection;

// use Reflection to enumerate some basic properties of a type...

namespace Example1
{
    class MyClass
    {
        private int MyField = 0;
        public void MyMethod1() { return; }
        public int MyMethod2(int i) { return i; }
        public int MyProperty { get { return MyField; } }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Reflection Demo Example 1");

            MyClass mc = new MyClass();
            Type t = mc.GetType();
            Console.WriteLine("Type Name: {0}", t.Name);
    
            foreach(MethodInfo m in t.GetMethods())
                Console.WriteLine("Method Name: {0}", m.Name);

            foreach (PropertyInfo p in t.GetProperties())
                Console.WriteLine("Property Name: {0}", p.Name);
        }
    }
}
輸出
Reflection Demo Example 1
Type Name: MyClass
Method Name: MyMethod1
Method Name: MyMethod2
Method Name: get_MyProperty
Method Name: GetType
Method Name: ToString
Method Name: Equals
Method Name: GetHashCode
Property Name: MyProperty
首先須要注意的是,在類定義中,乍看起來講明方法的篇幅比我預期的要多不少。這些額外的方法是從哪裏來的呢?任何精通 .NET Framework 對象層次結構的人,都會識別從通用基類 Object 自身繼承的這些方法。(事實上,我首先使用了 Object.GetType 檢索其類型。)此外,您能夠看到屬性的 getter 函數。如今,若是您只須要 MyClass 自身顯式定義的函數,該怎麼辦呢?換句話說,您如何隱藏繼承的函數?或者您可能只須要顯式定義的實例函數?
隨便在線看看 MSDN ®,就會發現你們都願意使用 GetMethods 第二個重載方法,它接受 BindingFlags 參數。經過結合來自 BindingFlags 枚舉中不一樣的值,您可讓函數僅返回所需的方法子集。替換 GetMethods 調用,代之以:
GetMethods(BindingFlags.Instance | BindingFlags.DeclaredOnly | 
           BindingFlags.Public)
結果是,您獲得如下輸出(注意這裏不存在靜態幫助器函數和繼承自 System.Object 的函數)。
Reflection Demo Example 1
Type Name: MyClass
Method Name: MyMethod1
Method Name: MyMethod2
Method Name: get_MyProperty
Property Name: MyProperty
若是您事先知道類型名稱(徹底限定)和成員,又該如何?您如何完成從枚舉類型向檢索類型的轉換? 圖 3中的示例顯示瞭如何經過 Object.GetType 和 Type.GetMethod,使用描述類型信息的字符串文字,檢索實際代碼對應項。有了前兩個示例中的代碼,您已經有了可以實現基元類瀏覽器的基本組件。經過名稱您能夠找到一個運行時實體,而後枚舉其各類相關屬性。
  Figure 3 經過字符串檢索類型和 MethodInfo
using System;
using System.Reflection;

// use Reflection to retrieve references to a type and method via name

namespace Example2
{
    class MyClass
    {
        public void MyMethod1() { return; }
        public void MyMethod2() { return; }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(「Reflection Demo Example 2」);

            // note that we must use the fully qualified name...
            Type t = Type.GetType(「Example2.MyClass」);
            MethodInfo m = t.GetMethod(「MyMethod1」);

            Console.WriteLine(「Type Name: {0}」, t.Name);
            Console.WriteLine(「Method Name: {0}」, m.Name);
        }
    }
}

動態調用代碼
迄今爲止,我已經得到運行時對象的句柄(如類型和方法),僅做描述用,例如輸出它們的名稱。可是如何作得更多呢?如何實際調用某個方法呢? 圖 4 顯示瞭如何得到某類型成員的 MethodInfo,而後使用 MethodInfo.Invoke 實際動態調用此方法。
  Figure 4 動態調用方法
using System;
using System.Reflection;

// use Reflection to retrieve a MethodInfo for an 
// instance method and invoke it upon many object instances

namespace Example3
{
    class MyClass
    {
        private int id = -1;

        public MyClass(int id) { this.id = id; }

        public void MyMethod2(object p)
        {
            Console.WriteLine(
                「MyMethod2 is being invoked on object with 「 +
                「id {0} with parameter {1}...」, 
                    id.ToString(), p.ToString());
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(「Reflection Demo Example 3」);

            MyClass mc1 = new MyClass(1);
            MyClass mc2 = new MyClass(2);
    
            Type t = mc1.GetType();
            MethodInfo method = t.GetMethod(「MyMethod2」);

            for(int i = 1; i <= 5; i++)
                method.Invoke(mc2, new object[]{i});
        }
    }
}
此例的幾個要點是:首先,從一個 MyClass, mc1 實例檢索一個 System.Type 實例,而後,從該類型檢索一個 MethodInfo 實例。最後,當調用 MethodInfo 時,經過把它做爲調用的第一個參數來傳遞,將其綁定到另外一個 MyClass (mc2) 實例中。
前面講過,對於您預期在源代碼中見到的類型和對象使用之間的區別,這個示例使這種區別變得模糊。邏輯上,您檢索了一個方法的句柄,而後調用該方法,就象它屬於一個不一樣的對象同樣。對於熟悉函數式編程語言的程序員來講,這可能垂手可得;但對於只熟悉 C# 的程序員來講,要分離對象實現和對象實例化,可能就不是那麼直觀了。

組合在一塊兒
至此我已經探討過檢查和調用的基本原理,接下來我會用具體的例子把它們組合在一塊兒。設想您但願交付一個庫,帶有必須處理對象的靜態幫助器函數。但在設計的時候,您對這些對象的類型沒有任何概念!這要看函數調用方的指示,看他但願如何從這些對象中提取有意義的信息。函數將接受一個對象集合,和一個方法的字符串描述符。而後它將遍歷該集合,調用每一個對象的方法,用一些函數聚合返回值(請參見 圖 5)。
  Figure 5 從對象中提取信息
using System;
using System.Collections.Generic;
using System.Reflection;

namespace Example4
{
    class Program
    {
        static void Main(string[] args)
        {
            // prepare some objects for our function to process
            object[] objs = new object[] {
                new IntReturner(1), new IntReturner(2), 
                new IntReturner(3), new SonOfIntReturner(4), 
                new SonOfIntReturner(5), new SonOfIntReturner(6),
            };

            Console.WriteLine(
                「Attempting to compute average, 「 + 
                「passing in array with {0} elements.」, objs.Length);

            int average = ComputeAverage(objs, 「GetInteger」);

            Console.WriteLine(「Found an average of {0}!」, average);
        }

        public static int ComputeAverage( 
            IEnumerable<object> objs, string methodname)
        {
            int sum = 0, count = 0;

            Type firstType = null;
            MethodInfo firstMethod = null;

            foreach (object o in objs)
            {
                if (firstMethod == null)
                {
                    firstType = o.GetType();
                    firstMethod = firstType.GetMethod(methodname);
                }

                sum += (int)firstMethod.Invoke(o, null);
                count++;
            }

            // note that we use integer division here (not floating point)
            if (count == 0) return 0;
            return sum / count; 
        }
    }

    class IntReturner
    {
        protected int value = -1;
        public IntReturner(int i) { value = i; }
        public virtual int GetInteger()
        {
            Console.WriteLine(
                「GetInteger called on instance of IntReturner, 「
                「I’m returning {0}!」, value);
            return value;
        }
    }

    class SonOfIntReturner : IntReturner
    {
        public SonOfIntReturner(int i) : base(i) { }
        public override int GetInteger()
        {
            Console.WriteLine(
                「GetInteger called on instance of SonOfIntReturner, 「
                「I’m returning {0}!」, this.value);
            return value;
        }
    }

    class EnemyOfIntReturner
    {
        protected int value = -1;
        public EnemyOfIntReturner(int i) { value = i; }
        public virtual int GetInteger()
        {
            Console.WriteLine(
                「GetInteger called on instance of EnemyOfIntReturner, 「
                「I’m returning {0}!」, value);
            return value;
        }
    }
}
就此例而言,我要聲明一些約束條件。首先,字符串參數描述的方法(必須由每一個對象的底層類型實現)不會接受任何參數,並將返回一個整數。代碼將遍歷對象集合,調用指定的方法,逐步計算出全部值的平均值。最後,由於這不是生產代碼,在求和的時候我不用擔憂參數驗證或整數溢出。
在瀏覽示例代碼時,能夠看到主函數與靜態幫助器 ComputeAverage 之間的協議除了對象自身的通用基類以外,並不依賴任何類型信息。換句話說,您能夠完全改變正在傳送的對象的類型和結構,但只要老是能使用字符串描述一個方法,且該方法返回整數,ComputeAverage 就能夠正常工做!
須要注意的一個關鍵問題跟隱藏在最後這個例子中的 MethodInfo(通常反射)有關。注意,在 ComputeAverage 的 foreach 循環中,代碼只從集合中的第一個對象中抓取一個 MethodInfo,而後綁定用於全部後續對象的調用。正如編碼所示,它運行良好 — 這是 MethodInfo 緩存的一個簡單例子。但此處有一個根本性的侷限。MethodInfo 實例僅能由其檢索對象同等層級類型的實例調用。由於傳入了 IntReturner 和 SonOfIntReturner(繼承自 IntReturner)的實例,才能這樣運行。
在示例代碼中,已經包含了名爲 EnemyOfIntReturner 的類,它實現了與其餘兩個類相同的基本協議,但並無共享任何常見共享類型。換句話說,該接口邏輯上等同,但在類型層級上沒有重疊。要探討 MethodInfo 在該情形下的使用,請嘗試向集合添加其餘對象,經過「new EnemyOfIntReturner(10)」獲得一個實例,再次運行示例。您會遇到一個異常,指出 MethodInfo 不能用於調用指定的對象,由於它和得到 MethodInfo 時的原始類型徹底無關(即便方法名稱和基本協議是等同的)。要使您的代碼達到生產水準,您須要作好遇到這一情形的準備。
一個可能的解決方案能夠是經過本身分析全部傳入對象的類型,保留對其共享的類型層級(若是有)的解釋。若是下一對象的類型與任意已知類型層級相異,就須要獲取和存儲一個新的 MethodInfo。另外一解決方案是捕獲 TargetException,並從新獲取一個 MethodInfo 實例。這裏提到的兩種解決方案都各有其優缺點。Joel Pobar 爲本雜誌 2007 五月期寫過一篇 優秀的文章,內容關於 MethodInfo 緩衝和我所極力推薦的反射性能。
但願此示例演示的嚮應用程序或框架中添加反射,能夠爲往後的自定義或可擴展性增長更多的靈活性。不能否認,較之本機編程語言中的同等邏輯,使用反射可能會有些繁瑣。若是您感到對您或您的客戶來講,向代碼中添加基於反射的後期綁定過於麻煩(畢竟他們須要以某種方式在您的框架中說明他們的類型和代碼),那麼可能僅須要適度的靈活性以取得某種平衡。

序列化的高效類型處理
至此咱們已經過若干示例講述了 .NET 反射的基本原理,接下來讓咱們看一下現實世界中的情形。若是您的軟件經過 Web 服務或其餘進程外遠程技術與其餘系統進行交互,那麼您極可能已經遇到序列化問題。序列化本質上是將活動的、佔用內存的對象,轉變成適合線上傳輸或磁盤存儲的數據格式。
.NET Framework 中的 System.Xml.Serialization 命名空間提供了擁有 XmlSerializer 的強大序列化引擎,它可使用任意託管對象,並將其轉換成 XML(往後也可將 XML 數據轉換回類型化的對象實例,這一過程稱之爲反序列化)。XmlSerializer 類是一種強大的、企業就緒的軟件片段,若是您在項目中面臨序列化問題,它將是您的首選。但爲了教學目的,咱們來探討如何實現序列化(或者其餘相似的運行時類型處理實例)。
設想情形:您正在交付一個框架,須要使用任意用戶類型的對象實例,並將其轉換成某種智能型數據格式。例如,假定有一個駐留內存的對象,類型爲以下所示的 Address:
(pseudocode)
class Address
{
    AddressID id;
    String Street, City;
    StateType State;
    ZipCodeType ZipCode;
}
如何生成適當的數據表示形式以方便往後使用?或許一個簡單的文本呈現將解決這一問題:
Address: 123
    Street: 1 Microsoft Way
    City: Redmond
    State: WA
    Zip: 98052
若是事先徹底瞭解須要轉換的正式數據類型(例如本身編寫代碼時),事情就變得很是簡單:
foreach(Address a in AddressList)
{
    Console.WriteLine(「Address:{0}」, a.ID);
    Console.WriteLine(「\tStreet:{0}」, a.Street);
    ... // and so on
}
然而,若是預先不知道在運行時會遇到的數據類型,狀況會變得十分有趣。您如何編寫象這樣的通常框架代碼?
MyFramework.TranslateObject(object input, MyOutputWriter output)
首先,您須要決定哪些類型成員對序列化有用。可能的狀況包括僅捕獲特定類型的成員,例如基元系統類型,或提供一種機制以供類型做者說明哪些成員須要被序列化,例如在類型成員上使用自定義屬性做爲標記)。您僅能夠捕獲特定類型的成員,例如基元系統類型,或類型做者可以說明哪些成員須要被序列化(可能的方法是在類型成員上使用自定義屬性做爲標記)。
一旦記錄清楚須要轉換的數據結構成員,您接着須要作的是編寫邏輯,從傳入的對象枚舉和檢索它們。反射在這裏擔負了繁重的任務,讓您既能夠查詢數據結構又能夠查詢數據值。
出於簡單性考慮,咱們來設計一個輕型轉換引擎,獲得一個對象,獲取全部其公共屬性值,經過直接調用 ToString 將它們轉換成字符串,而後將這些值序列化。對於一個名爲「input」的給定對象,算法大體以下:
  1. 調用 input.GetType 以檢索 System.Type 實例,該實例描述了 input 的底層結構。
  2. 用 Type.GetProperties 和適當的 BindingFlags 參數,將公共屬性做爲 PropertyInfo 實例檢索。
  3. 使用 PropertyInfo.Name 和 PropertyInfo.GetValue,將屬性做爲鍵-值對檢索。
  4. 在每一個值上調用 Object.ToString 將其(經過基本方式)轉化爲字符串格式。
  5. 將對象類型的名稱和屬性名稱、字符串值的集合打包成正確的序列化格式。
這一算法明顯簡化了事情,同時也抓住了獲得運行時數據結構,並將其轉化爲自描述型數據的要旨。但這裏有一個問題:性能。以前提到,反射對於類型處理和值檢索的成本都很高。本示例中,我在每一個提供類型的實例中執行了完整的類型分析。
若是以某種方式能夠捕獲或保留您對於類型結構的理解,以便往後不費力地檢索它,並有效處理該類型的新實例;換句話說,就是往前跳到示例算法中的步驟 #3?好消息是,利用 .NET Framework 中的功能,徹底可能作到這一點。一旦您理解了類型的數據結構,即可以使用 CodeDom 動態生成綁定到該數據結構的代碼。您能夠生成一個幫助器程序集,其中包含幫助器類和引用了傳入類型並直接訪問其屬性的方法(相似託管代碼中的任何其餘屬性),所以類型檢查只會對性能產生一次影響。
如今我將修正這一算法。新類型:
  1. 得到對應於該類型的 System.Type 實例。
  2. 使用各類 System.Type 訪問器檢索架構(或至少檢索對序列化有用的架構子集),例如屬性名稱、字段名稱等。
  3. 使用架構信息生成幫助器程序集(經過 CodeDom),該程序集與新類型相連接,並有效地處理實例。
  4. 在幫助器程序集中使用代碼,提取實例數據。
  5. 根據須要序列化數據。
對於給定類型的全部傳入數據,能夠往前跳到步驟 #4,較之顯式檢查每一實例,這麼作能夠得到巨大的性能提高。
我開發了一個名爲 SimpleSerialization 的基本序列化庫,它用反射和 CodeDom(本專欄中可下載)實現了這一算法。主要組件是一個名爲 SimpleSerializer 的類,是用戶用一個 System.Type 實例構造所得。在構造函數中,新的 SimpleSerializer 實例會分析給定的類型,利用幫助器類生成一個臨時程序集。該幫助器類會緊密綁定到給定的數據類型,並且對實例的處理方式就象本身在徹底事先了解類型的狀況下編寫代碼那樣。
SimpleSerializer 類有以下佈局:
class SimpleSerializer
{
    public class SimpleSerializer(Type dataType);

    public void Serialize(object input, SimpleDataWriter writer);
}
簡單地使人驚歎!構造函數承擔了最繁重的任務:它使用反射來分析類型結構,而後用 CodeDom 生成幫助器程序集。SimpleDataWriter 類只是用來闡明常見序列化模式的數據接收器。
要序列化一個簡單的 Address 類實例,用下面的僞代碼便可完成任務:
SimpleSerializer mySerializer = new SimpleSerializer(typeof(Address));
SimpleDataWriter writer = new SimpleDataWriter();
mySerializer.Serialize(addressInstance, writer);

結束
強烈建議您親自試用一下示例代碼,尤爲是 SimpleSerialization 庫。我在 SimpleSerializer 一些有趣的部分都添加了註釋,但願可以有所幫助。固然,若是您須要在產品代碼中進行嚴格的序列化,那麼確實要依靠 .NET Framework 中提供的技術(例如 XmlSerializer)。但若是您發如今運行時須要使用任意類型並能高效處理它們,我但願您採用個人 SimpleSerialization 庫做爲本身的方案。
對 CLR 開發人員 Weitao Su(反射)和 Pete Sheill (CodeDom) 所提供的指導和反饋,在此深表謝意。

請將您想詢問的問題和提出的意見發送至  clrinout@microsoft.com. 程序員


Mike Repass是 .NET Framework CLR 團隊的項目經理。他主要負責反射、CodeDom 以及執行引擎的各部分。您能夠經過他的博客  blogs.msdn.com/mrepass 與他聯繫。
相關文章
相關標籤/搜索