C#之你懂得的反射

前言:反射在C#中雖然不經常使用(若是不須要動態加載xx.dll),可是有時候倒是設計某個程序或者完成某類功能比較好用的技術。好比:一個支持動態擴展的程序,這樣就須要動態加載dll,動態建立加載dll的程序集,最終完成操做。編程

1、加載程序集數組

對於程序集的加載通常會使用兩個方法來進行:緩存

1.Assembly.Load(string assemblyName),AssemblyName爲程序集的長格式名稱。安全

      Assembly SampleAssembly = Assembly.Load("SampleAssembly, Version=1.0.2004.0, Culture=neutral, PublicKeyToken=8744b20f8da049e3");
        foreach (Type oType in SampleAssembly.GetTypes()) {
            Console.WriteLine(oType.Name);
        }

2.Assembly.Load(AssemblyName assemblyName),assemblyName爲完整描述程序集的惟一標識,經過Assembly.GetName()方法可獲得程序集的惟一標識。
app

3.Assembly.LoadFile(string path),加載指定路徑上的程序集文件的內容。ide

4.Assembly.LoadFrom(string assemblyFile),已知程序集的文件名或路徑,加載程序集。函數

       Assembly SampleAssembly;
           SampleAssembly = Assembly.LoadFile(@"C:\Users\Admin\Documents\visual studio 2013\Projects\ConsoleApplication3\Sample\bin\Debug\Sample.dll");
            MethodInfo Method = SampleAssembly.GetTypes()[0].GetMethod("DoWork");
            ParameterInfo[] Params = Method.GetParameters();
            foreach (ParameterInfo Param in Params)
            {
                Console.WriteLine("Param=" + Param.Name.ToString());
                Console.WriteLine("  Type=" + Param.ParameterType.ToString());
                Console.WriteLine("  Position=" + Param.Position.ToString());
                Console.WriteLine("  Optional=" + Param.IsOptional.ToString());
            }

LoadFile和LoadFrom方法在大部分狀況下是同樣的結果,可是仍是有區別的,以下:工具

1、Assembly.LoadFile只載入相應的dll文件,好比Assembly.LoadFile("a.dll"),則載入a.dll,假如a.dll中引用了b.dll的話,b.dll並不會被載入。
Assembly.LoadFrom則不同,它會載入dll文件及其引用的其餘dll,好比上面的例子,b.dll也會被載入。
2、用Assembly.LoadFrom載入一個Assembly時,會先檢查前面是否已經載入過相同名字的Assembly,好比a.dll有兩個版本(版本1在目錄1下,版本2放在目錄2下),程序一開始時載入了版本1,當使用Assembly.LoadFrom("2\\a.dll")載入版本2時,不能載入,而是返回版本1。Assembly.LoadFile的話則不會作這樣的檢查。

 除了上述使用外,還能夠從一個url中加載一個dll文件:性能

SampleAssembly = Assembly.LoadFrom("http://http://www.cnblogs.com/ListenFly/Sample.dll");

雖然此地址不存在,可是此方式是可行的。ui

5.僅僅加載程序集

若是構建的工具只是經過反射來分析程序元數據,並但願確保程序集中的任何代碼都不會執行,那麼加載程序集的最佳方式就是使用Assembly的ReflectionOnlyLoadFrom方法或者使用ReflectionOnlyLoad。

ReflectionOnlyLoadFrom方法將加載由路徑指定的文件;和Load不一樣的是,ReflectionOnlyLoad方法不會應用版本控制策略,因此你指定的是哪一個版本,得到的就是哪一個版本。

用ReflectionOnlyLoadFrom或ReflectionOnlyLoad方法加載程序集時,CLR禁止程序集中的任何代碼執行;試圖執行由這兩個方法加載的程序集中的代碼,會致使CLR拋出異常。

6.將dll和exe打包在一塊兒,並加載

有時候咱們只會發佈一個exe,甚至說不用安裝直接運行就能夠的軟件,這時候就能夠將要引用的dll添加到項目中,而且設置文件的屬性(Properties)中的Build Action(生成操做)設置爲嵌入資源(Embed Resource)。在運行時,CLR會找不到依賴的DLL程序集。爲了解決這個問題,當應用程序初始化時,向AppDomain的ResolveAssembly事件登記一個回調方法,以下:

      AppDomain.CurrentDomain.AssemblyResolve +=(sender, e) =>
                {
                    string resourceName = "AssemblyLoadingAndReliection." +
                        new AssemblyName(e.Name) + ".dll";
                    using (var stream=Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName))
                    {
                        Byte[] assemblyData=new Byte[stream.Length];
                        stream.Read(assemblyData, 0, assemblyData.Length);
                        return Assembly.Load(assemblyData);
                    }
                };

如今,一個線程首次調用一個方法時,若是發現該方法引用了依賴DLL文件中的一個類型,就會引起一個AssemblyResolve事件,而上述回調代碼會找到所需的嵌入DLL資源,並調用Assembly的Load方法的一個重載版本(傳遞一個Byte[]實參),從而加載所需的資源。

2、關於反射的性能

雖然反射至關的強大,容許在運行時發現並使用編譯時還不瞭解的類型及其成員。可是有兩個缺點:

1.反射會形成編譯時沒法保證類型安全性,因爲反射要嚴重依賴字符串,因此會喪失編譯時的類型安全性。

2.反射速度慢。使用反射時,類型及其成員的名稱在編譯時未知;要用字符串名稱標識每一個類型及其成員,以便在運行時發現他們。也就是說,須要掃描程序集的元數據,而且要不斷地執行字符串。

基於上述全部緣由,最好避免利用發射來訪問字段或者調用方法/屬性。若是要寫一個應用程序來動態發現和構造類型實例,應採起如下兩種技術之一。

1.讓類型從一個編譯時已知的基類型派生。在運行時,構造派生類型的一個實例,將對它的引用放到基類型的一個變量中,再調用基類型定義的虛方法。

2.讓類型實現一個編譯時已知的接口。在運行時,構造派生類型的一個實例,將對它的引用放到接口的一個變量中,再調用接口定義的方法。

方法1能夠用來控制類的版本,由於隨時都能向基類添加一個成員,派生類則直接繼承;方法2則是從功能上選擇一個比較好的對象來操做。

3、反射程序集中的類型

1.發現程序集中的類型

反射常常用於判斷一個程序集中定義了哪些類型。最經常使用的方法是Assembly的GetExportedTypes。

        string assemblyName = @"System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089";
            Assembly assembly = Assembly.Load(assemblyName);
            foreach (var t in assembly.GetExportedTypes())
            {
                Console.WriteLine(t.FullName);
            }

 2.GetType方法

Object.GetType方法返回當前類型的RuntimeType對象的一個引用。此方法能夠用來判斷當前類型是不是某種類型:

if(o.GetType() == typeof(MyType))

除了上述方式  is 關鍵字也能夠用來判斷對象是不是某種類型。

 3.構造類型的實例

  •  System.Activator的CreateInstance方法,Activator提供了CreateInstance靜態方法,此方法能夠傳遞一個Type對象,也能夠傳遞標識了想要建立的類型的一個string。
    string的重載,第一個參數爲程序集名稱,第二個參數爲類型名稱(徹底限定名,即namespace+typeName)。
       ObjectHandle handle = Activator.CreateInstance("PersonInfo", "Person");
        Person p = (Person) handle.Unwrap();

     type參數重載,調用指定類型的默認構造函數。

    objct o= Activator.CreateInstance(typeof(object));

    上述方法返回的不是新對象的引用,而是一個System.Runtime.Remoting.ObjectHanlde對象(派生自System.MarshalByRefObject)。ObjectHandle類型容許將一個AppDomain中建立的對象傳至其餘AppDomain,期間不強迫對象具體化。因此要具體化對象時,請調用ObjectHandle的Unwrap方法。

  • System.Activator的CreateInstanceFrom方法,Activator提供了一組靜態的CreateInstanceFrom方法。這些方法與CreateInstance行爲類似,只是必須經過字符串來指定類型及其程序集。一樣要使用ObjectHandle的Unwrap方法進行具體化對象。
  • System.AppDomain的方法 AppDomain提供了4個用於構造類型實例的實例方法:CreateInstance、CreateInstanceAndUnwrap、CreateInstanceFrom以及CreateInstanceFromAndUnwrap。這些方法的行爲和Activator的行爲相似,只是它們都是實例方法,容許指定在哪一個AppDomain中構造對象。另外,帶Unwrap後綴的方法還能簡化操做。
  • System.Type的InvokeMember實例方法,可使用一個Type對象引用來調用InvokeMember方法。System.Reflection.ConstructorInfo的Invoke實例方法,使用一個Type對象引用,能夠綁定到一個特定的構造器,並獲取對構造器的ConstructorInfo對象的一個引用。而後能夠利用對這個ConstructorInfo對象的引用調用它的Invoke方法。


構造泛型:

       Type openType = typeof(Dictionary<,>);
            Type closedType = openType.MakeGenericType(typeof(string), typeof(int));
            object o = Activator.CreateInstance(closedType);
            Console.WriteLine(o.GetType());

 4.設計一個支持加載項的應用程序

一個類庫Host,包含本身的接口

namespace Host
{
    public interface IMyInstance
    {
        string DoWork(int parameter);
    }
}

實現接口的兩個類(在類庫Sample中定義):

public class MyClass1 : IMyInstance
    {
        public string DoWork(int parameter)
        {
            return "Class1:" + parameter.ToString();
        }
    }
 public class MyClass2:IMyInstance
    {
        public string DoWork(int parameter)
        {
            return "Class2:" + parameter.ToString();
        }
    }

 加載程序所引用的dll文件,並加載全部dll的程序集,最終找到實現自IMyInstance接口的類:

  static void Main(string[] args)
        {
            //獲取當前運行程序的路徑
            var hostDir = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
            //加載路徑中的全部dll
            string[] hostAssemblies = Directory.GetFiles(hostDir, "*.dll");
            List<Type> hostTypes = new List<Type>();

            foreach (var file in hostAssemblies)
            {
                //將dll文件加載到程序集中
                Assembly hostAssembly = Assembly.LoadFrom(file);
                foreach (var type in hostAssembly.GetExportedTypes())
                {
                    //肯定type爲類而且繼承自(實現)IMyInstance
                    if (type.IsClass && typeof(IMyInstance).IsAssignableFrom(type))
                        hostTypes.Add(type);
                }
            }

            foreach (var type in hostTypes)
            {
                IMyInstance instance = (IMyInstance)Activator.CreateInstance(type);
                Console.WriteLine(instance.DoWork(10));
            }

            Console.Read();
        }

當前假設,Main函數所在的程序,添加了Host和Sample類庫,因此在Debug中有兩個dll文件。

4、使用反射發現類型的成員

1.發現類型成員

字段、構造器、方法、屬性、事件和嵌套類型均可以被定義爲一個類型的成員。FCL包含一個名爲System.Reflection.MemberInfo類型。此類型爲抽象類,封裝一組全部類型成員的通用屬性。從MemberInfo派生的是一組類,每一個類都封裝了與一個特定類型成員相關的更多屬性。

  static void Main(string[] args)
        {
            Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
            foreach (var assembly in assemblies)
            {
                WritLine(0, "Assembly:{0}", assembly);
                foreach (var type in assembly.GetExportedTypes())
                {
                    BindingFlags flags = BindingFlags.DeclaredOnly | BindingFlags.NonPublic |
                        BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static;
                    foreach (var memberInfo in type.GetMembers(flags))
                    {
                        var typeName = string.Empty;
                        if (memberInfo is Type) typeName = "Nested Type";
                        else if (memberInfo is FieldInfo) typeName = "FieldInfo";
                        else if (memberInfo is MethodInfo) typeName = "MethodInfo";
                        else if (memberInfo is ConstructorInfo) typeName = "ConstructorInfo";
                        else if (memberInfo is PropertyInfo) typeName = "PropertyInfo";
                        else if (memberInfo is EventInfo) typeName = "EventInfo";
                        WritLine(2, "{0}: {1}", typeName, memberInfo);
                    }
                }
            }
            Console.Read();
        }
        static void WritLine(int indent, string format, params object[] args)
        {
            Console.WriteLine(new string(' ', 3 * indent) + format, args);
        }

上述爲加載當前AppDomain中的全部程序集、以及其類型和類型的成員,並輸出。

 2.BindingFlags篩選返回的成員種類

可使用Type的GetMembers、GetNextedTypes、GetFields、GetConstructors、GetMethods、GetProperties、GetEvents方法查詢一個類型的成員。使用上述方法時,能夠傳遞BindingFlags枚舉類型的一個實例。這個類型標識了一組經過邏輯OR運算合併到一塊兒的位標識(經過在枚舉添加  [Flags]實現),默認設置是BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static(若是指定了Public或者NonPublic,那麼必須同時指定Instance | Static,不然將不返回成員)。

3.發現類型的接口

經過Type類型的FindInterfaces、GetInterface或者GetInterfaces方法。全部這些方法都返回表明接口的Type對象。爲了得到一個特定接口的MethodInfo對象,能夠調用Type的GetInterfaceMap實例方法(傳遞接口類型做爲參數)。該方法返回System.Reflection.InterfaceMapping的一個實例。

InterfaceMapping類型的公共字段:

TargetType Type類型 用於調用GetInterfaceMapping的類型

InterfaceType Type類型 傳給GetInterfaceMapping的接口類型

InterfaceMethods MethodInfo[]類型 一個數組,每一個元素表示接口中的一個方法的信息

TargetMethods    MethodInfo[]類型 一個數組,每一個元素表示由當前類型實現自接口的一個方法

InterfaceMethods和TargetMethods數組是相互對應的。也就是說InterfaceMethods[0]表示的是接口的MethodInfo,那麼TargetMethods[0]表示的是類型定義的方法(實現自接口)。

下面一個發現類型實現的接口的例子:

   interface IBookRetailer:IDisposable
    {
        void Purchase();
        void ApplyDiscount();
    }
    interface IMusicRetailer
    {
        void Pruchase();
    }
 public class MyRetailer:IBookRetailer,IMusicRetailer
    {
        /// <summary>
        /// MyRetailer類本身的Purchase方法
        /// </summary>
        public void Purchase()
        {
        }

        /// <summary>
        /// IMusicRetailer的Pruchase方法
        /// </summary>
        void IMusicRetailer.Pruchase()
        {
        }

        /// <summary>
        /// IBookRetailer的Purchase方法
        /// </summary>
        void IBookRetailer.Purchase()
        {
        }

      public  void ApplyDiscount()
        {
        }

        public void Dispose()
        {
        }
    }

上述代碼定義了兩個接口一個類,而後讓這個類實現兩個接口,這點都沒有任何特殊。特殊就在於,兩個接口擁有一樣的一個方法就是Pruchase,同時類自己也有一個這樣名稱的方法,防止出現方法的衝突,這裏使用了顯示實現接口,即方法的前綴爲具體的接口,這樣雖然在類中有3個Pruchase方法,可是因爲是指定了其所屬接口,因此是沒有問題的。

 static void Main(string[] args)
        {
            Type type = typeof(MyRetailer);
            Type[] interfaces = type.FindInterfaces(TypeFilter, typeof(Program).Assembly);
            Console.WriteLine("MyRetailer implements the following " +
                " instances (defined in this assemly) :");
            foreach (var interfaceType in interfaces)
            {
                Console.WriteLine("\nInterface:" + interfaceType);
                //獲取映射接口方法的類型的方法
                InterfaceMapping mapping = type.GetInterfaceMap(interfaceType);

                for (int i = 0; i < mapping.InterfaceMethods.Length; i++)
                {
                    Console.WriteLine(" {0} is Implemented by {1}", mapping.InterfaceMethods[i], mapping.TargetMethods[i]);
                }
            }
            Console.Read();
        }

        /// <summary>
        /// 若是類型匹配篩選器條件,就返回true
        /// </summary>
        /// <param name="t"></param>
        /// <param name="filterCriteria"></param>
        /// <returns></returns>
        private static bool TypeFilter(Type t, object filterCriteria)
        {
            //若是接口和filterCriteria標識的程序集中定義的,就返回true
            return t.Assembly == filterCriteria;
        }

上述代碼用於查找MyRetailer的全部接口,FindInterfaces有兩個參數,第一個爲返回值爲bool類型的過濾方法,第二個參數爲方法的參數。咱們的TypeFilter即爲第一個方法,有兩個參數,一個爲Type(自動判斷,根據當前調用此方法的類型的接口類型),第二個就是參數了。在方法中判斷,只有接口的程序集和指定的程序集一直才認爲是true。
經過FindInterfaces獲得Type數組,而後使用Type.GetInterfaceMap獲得接口和類型的映射,最後根據InterfaceMethods和TargetMethods輸出實現的信息。

P.S. 是否是發現輸出的信息中並無IDisposable接口的信息,那是由於在TypeFilter方法中咱們過濾了和指定的程序集不一致的接口,而IDisposable固然不和Exe在同一個程序集,因此就沒有查找到。

4.調用類型的成員

經過反射獲得類型的成員是沒有太多意義的,咱們更多地是去操做成員。好比,能夠調用FieldInfo進行設置或獲取字段的值;調用ConstructorInfo,訪問構造函數,並得到實例;也能夠調用一個MethodInfo,進行執行方法,若是有返回值則能夠獲得;PropertyInfo能夠調用屬性的get和se方法;EventInfo能夠添加或者刪除一個事件。

上述的全部操做,咱們均可以使用Type.InvokeMember方法進行實現:

  public object InvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, object[] args, CultureInfo culture);

name:成員名稱;invokeAttr:如何查找成員;binder:如何匹配成員和實參;target:要調用其成員的對象;args:要傳給方法的實參;culture:某些綁定器使用的語言文化。

binder表示執行InvokeMember方法時選擇成員時候使用的規則,從候選者列表中選擇一個成員,並執行實參類型到形參類型的類型轉換,若是傳遞null則默認使用DefauleBinder,固然咱們能夠本身定義新的Binder(詳見MSDNBinder類介紹)。

下表列出了默認聯編程序支持的轉換。

 

源類型

目標類型

任何類型

它的基類型。

任何類型

它實現的接口。

Char

Unt1六、UInt3二、Int3二、UInt6四、Int6四、Single、Double

Byte

Char、Unt1六、Int1六、UInt3二、Int3二、UInt6四、Int6四、Single、Double

SByte

Int1六、Int3二、Int6四、Single、Double

UInt16

UInt3二、Int3二、UInt6四、Int6四、Single、Double

Int16

Int3二、Int6四、Single、Double

UInt32

UInt6四、Int6四、Single、Double

Int32

Int6四、Single、Double

UInt64

Single、Double

Int64

Single、Double

Single

Double

5.一次綁定,屢次調用

使用Type的InvokeMember方法能夠訪問一個類型的全部成員。可是,應該注意,每次調用InvokeMember方法時,它都必須綁定到一個特定的成員,而後才能調用它。若是每次調用一個成員都進行這個操做,那麼性能確定是會受到影響。因此,若是打算頻繁訪問一個成員,最好是一次綁定,屢次調用。

綁定成員後,若是調用成員:

FieldInfo  調用GetValue獲取字段的值;調用SetValue設置字段的值。

ConstructorInfo 調用Invoke構造類型的一個實例,並調用構造函數。

MethodInfo 調用Invoke調用類型的一個方法。

PropertyInfo 調用GetValue調用屬性的get訪問器方法;調用SetValue調用屬性的set構造器方法。

EventInfo 調用AddEventHandler調用事件的add訪問器方法;調用RemoveEventHanlder調用事件的remove訪問器方法。

此外,EventInfo還提供了GetAddMethod和GetRemoveMethod方法,都返回一個MethodInfo,這個MethodInfo對應事件添加或刪除委託的方法。要添加或刪除一個委託,可調用這些MethodInfo對象,也可調用EventInfo類型提供的AddEventhandler和RemoveEventHanlder方法。

下面例子演示了使用反射來範文類型成員的各類方式:

SomeType類:

  class SomeType
    {
        private int m_someField;
        public SomeType(ref int x) { x *= 2; }
        public override string ToString()
        {
            return m_someField.ToString();
        }

        public int SomeProp
        {
            get { return m_someField; }
            set
            {
                if (value < 1)
                    throw new ArgumentOutOfRangeException("值不在範圍以內");
                m_someField = value;
            }
        }

        public event EventHandler SomeEvent;
        private void NoCompilerWarnings()
        {
            SomeEvent.ToString();
        }
    }

此類有多個成員,一個私有變量,一個公共屬性,一個公共構造器(以引用方式傳遞的int類型參數),一個公用方法,以及一個公共事件。

一共使用四種方式來訪問SomeType的成員:

  • UseInvokeMemberToBindAndInvokeTheMember方法演示了利用Type的InvokeMember來綁定並調用一個成員。
  • BindToMemberThenInvokeTheMember方法演示瞭如何綁定到一個成員,並在之後調用它。若是打算在不一樣對象上屢次調用同一個成員,那麼這個方法能夠提升性能。
  • BindToMemberCreateDelegateToMemberThenInvokeTheMember方法演示瞭如何綁定到一個對象或成員,而後建立一個委託來引用該對象或成員。經過委託來調用的速度很是快。若是想在相同的對象上屢次調用相同的成員,那麼此技術比上一個技術速度還要快。
  • UseDynamicToBindAndInvokeTheMember方法演示瞭如何使用C#的dynamic(.Net 4.0新特性)基元類型來簡化訪問成員時的語法。另外,若是打算在相同類型的不一樣對象上調用相同的成員,此技術性能還不錯,由於針對每一個類型,綁定都只會發生一次,並且能夠緩存起來,之後屢次調用時速度會很快。還可使用此技術調用不用類型的對象的成員。
    private static void UseInvokeMemberToBindAndInvokeTheMember(Type t)
        {
            Console.WriteLine("UseInvokeMemberToBindAndInvokeTheMember");

            //構造一個Type的實例
            object[] args = new object[] { 20 };
            Console.WriteLine("x before constructor called:" + args[0]);
            object obj = t.InvokeMember(null, flags | BindingFlags.CreateInstance, null, null, args);
            Console.WriteLine("Type: " + obj.GetType().ToString());
            Console.WriteLine("x after constructor returns:" + args[0]);

            //讀寫一個字段
            t.InvokeMember("m_someField", flags | BindingFlags.SetField, null, obj, new object[] { 25 });
            int value = Convert.ToInt16(t.InvokeMember("m_someField", flags | BindingFlags.GetField, null, obj, null));
            Console.WriteLine("someField:" + value);

            //調用一個方法
            string str = (string)t.InvokeMember("ToString", flags | BindingFlags.InvokeMethod, null, obj, null);
            Console.WriteLine("ToString:" + str);

            //讀寫一個屬性
            try
            {
                t.InvokeMember("SomeProp", flags | BindingFlags.SetProperty, null, obj, new object[] { 5 });
            }
            catch (TargetInvocationException)
            {
                Console.WriteLine("Set Property Catch!");
            }

            t.InvokeMember("SomeProp", flags | BindingFlags.SetProperty, null, obj, new object[] { 30 });
            value = Convert.ToInt16(t.InvokeMember("SomeProp", flags | BindingFlags.GetProperty, null, obj, null));
            Console.WriteLine("SomeProp:" + value);

            //調用事件add/remove方法,爲事件添加和刪除一個委託
            EventHandler handler = new EventHandler(EventCallback);
            t.InvokeMember("add_SomeEvent", flags | BindingFlags.InvokeMethod, null, obj, new object[] { handler });
            t.InvokeMember("remove_SomeEvent", flags | BindingFlags.InvokeMethod, null, obj, new object[] { handler });
        }

        private static void BindToMemberThenInvokeTheMember(Type t)
        {
            //構造一個實例,之因此GetConstructor的參數爲MakeByRefType,那是由於SomeType
            //的構造函數的參數爲ref引用傳遞
            ConstructorInfo constructorInfo = t.GetConstructor(new Type[] { typeof(Int32).MakeByRefType() });
            object[] args = new object[] { 20 };
            Console.WriteLine("x before constructor called: " + args[0]);
            object obj = constructorInfo.Invoke(args);
            Console.WriteLine("Type:" + obj.GetType().ToString());
            Console.WriteLine("x after constructor returns:" + args[0]);

            //讀寫一個字段
            FieldInfo fieldInfo = obj.GetType().GetField("m_someField",flags);
            fieldInfo.SetValue(obj, 25);
            Console.WriteLine("someField:" + fieldInfo.GetValue(obj));

            //調用一個方法
            MethodInfo methodInfo = obj.GetType().GetMethod("ToString", flags);
            string str = (string)methodInfo.Invoke(obj, null);
            Console.WriteLine("ToString:" + str);

            //讀寫一個屬性
            PropertyInfo propertyInfo = obj.GetType().GetProperty("SomeProp", typeof(Int32));
            try
            {
                propertyInfo.SetValue(obj, 5, null);
            }
            catch (Exception)
            {
                Console.WriteLine("Property set catch!");
            }

            propertyInfo.SetValue(obj, 30, null);
            Console.WriteLine("SomeProp:" + propertyInfo.GetValue(obj, null));

            //爲事件添加和刪除一個委託
            EventInfo eventInfo = obj.GetType().GetEvent("SomeEvent", flags);
            EventHandler handler = new EventHandler(EventCallback);
            eventInfo.AddEventHandler(obj, handler);
            eventInfo.RemoveEventHandler(obj, handler);

        }
      

        private static void BindToMemberCreateDelegateToMemberThenInvokeTheMember(Type t)
        {
            Console.WriteLine("BindToMemberCreateDelegateToMemberThenInvokeTheMember");

            //構造一個實例(不能建立對構造函數的委託)
            object[] args = new object[] { 20 };
            Console.WriteLine("x before constructor called:" + args[0]);
            object obj = Activator.CreateInstance(t, args);
            Console.WriteLine("Type:" + obj.GetType().ToString());
            Console.WriteLine("x after constructor returns:" + args[0]);

            //調用一個方法
            MethodInfo methodInfo = obj.GetType().GetMethod("ToString", flags);
            var toString = (Func<string>)Delegate.CreateDelegate(typeof(Func<string>), obj, methodInfo);
            string str = toString();
            Console.WriteLine("ToString:" + str);

            //讀寫一個屬性
            PropertyInfo propertyInfo = obj.GetType().GetProperty("SomeProp", typeof(Int32));
            var setSomeProp = (Action<Int32>)Delegate.CreateDelegate(typeof(Action<Int32>), obj, propertyInfo.GetSetMethod());
            try
            {
                setSomeProp(5);
            }
            catch (Exception)
            {
                Console.WriteLine("Property set catch!");
            }
            setSomeProp(60);
            var getSomeProp = (Func<Int32>)Delegate.CreateDelegate(typeof(Func<Int32>), obj, propertyInfo.GetGetMethod());
            Console.WriteLine("SomeProp:" + getSomeProp());

            //從事件中添加和刪除一個委託
            EventInfo eventInfo = obj.GetType().GetEvent("SomeEvent", flags);
            var addSomeEvent = (Action<EventHandler>)Delegate.CreateDelegate(typeof(Action<EventHandler>), obj, eventInfo.GetAddMethod());
            addSomeEvent(EventCallback);
            var removeSomeEvent = (Action<EventHandler>)Delegate.CreateDelegate(typeof(Action<EventHandler>), obj, eventInfo.GetRemoveMethod());
            removeSomeEvent(EventCallback);
        }

        private static void UseDynamicToBindAndInvokeTheMember(Type t)
        {
            Console.WriteLine("UseDynamicToBindAndInvokeTheMember");
            //構造一個實例(不能建立對構造器的委託)
            object[] args = new object[] { 20 };
            Console.WriteLine("x before constructor called:" + args[0]);
            dynamic obj = Activator.CreateInstance(t,args);
            Console.WriteLine("Type:" + obj.GetType().ToString());
            Console.WriteLine("x after constructor returns:" + args[0]);

            //讀寫一個字段
            try
            {
                obj.m_someField = 25;
                int value = Convert.ToInt16(obj.m_someField);
                Console.WriteLine("someField:" + value);
            }
            catch (Exception ex)
            {
                //字段是私有的,so會出錯的
                Console.WriteLine("Failed to access field:" + ex.Message);
            }

            //調用一個方法
            string str = obj.ToString();
            Console.WriteLine("ToString:" + str);

            //讀寫一個屬性
            try
            {
                obj.SomeProp =5;
            }
            catch (Exception)
            {

                Console.WriteLine("Property set catch!");
            }
            obj.SomeProp = 30;
            int v = Convert.ToInt16(obj.SomeProp);
            Console.WriteLine("SomeProp:", v);

            //添加刪除事件
            obj.SomeEvent += new EventHandler(EventCallback);
            obj.SomeEvent -= new EventHandler(EventCallback);
        }

        /// <summary>
        /// 事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private static void EventCallback(object sender, EventArgs e)
        {

        }

 好了,反射知識暫告一段落,但願多提意見和建議。

相關文章
相關標籤/搜索