• C#之你懂得的反射


    前言:反射在C#中虽然不常用(如果不需要动态加载xx.dll),但是有时候却是设计某个程序或者完成某类功能比较好用的技术。比如:一个支持动态扩展的程序,这样就需要动态加载dll,动态创建加载dll的程序集,最终完成操作。

    一、加载程序集

    对于程序集的加载一般会使用两个方法来进行:

    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()方法可得到程序集的唯一标识。

    3.Assembly.LoadFile(string path),加载指定路径上的程序集文件的内容。

    4.Assembly.LoadFrom(string assemblyFile),已知程序集的文件名或路径,加载程序集。

           Assembly SampleAssembly;
               SampleAssembly = Assembly.LoadFile(@"C:UsersAdminDocumentsvisual studio 2013ProjectsConsoleApplication3SampleinDebugSample.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");

    虽然此地址不存在,但是此方式是可行的。

    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[]实参),从而加载所需的资源。

    二、关于反射的性能

    虽然反射相当的强大,允许在运行时发现并使用编译时还不了解的类型及其成员。但是有两个缺点:

    1.反射会造成编译时无法保证类型安全性,由于反射要严重依赖字符串,所以会丧失编译时的类型安全性。

    2.反射速度慢。使用反射时,类型及其成员的名称在编译时未知;要用字符串名称标识每个类型及其成员,以便在运行时发现他们。也就是说,需要扫描程序集的元数据,并且要不断地执行字符串。

    基于上述所有原因,最好避免利用发射来访问字段或者调用方法/属性。如果要写一个应用程序来动态发现和构造类型实例,应采取以下两种技术之一。

    1.让类型从一个编译时已知的基类型派生。在运行时,构造派生类型的一个实例,将对它的引用放到基类型的一个变量中,再调用基类型定义的虚方法。

    2.让类型实现一个编译时已知的接口。在运行时,构造派生类型的一个实例,将对它的引用放到接口的一个变量中,再调用接口定义的方法。

    方法1可以用来控制类的版本,因为随时都能向基类添加一个成员,派生类则直接继承;方法2则是从功能上选择一个比较好的对象来操作。

    三、反射程序集中的类型

    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文件。

    四、使用反射发现类型的成员

    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("
    Interface:" + 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

    Unt16、UInt32、Int32、UInt64、Int64、Single、Double

    Byte

    Char、Unt16、Int16、UInt32、Int32、UInt64、Int64、Single、Double

    SByte

    Int16、Int32、Int64、Single、Double

    UInt16

    UInt32、Int32、UInt64、Int64、Single、Double

    Int16

    Int32、Int64、Single、Double

    UInt32

    UInt64、Int64、Single、Double

    Int32

    Int64、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)
            {
    
            }

     好了,反射知识暂告一段落,希望多提意见和建议。

  • 相关阅读:
    ATOM编辑器插件
    说说关于IE浏览器兼容性
    git命令
    Vue js 的生命周期详解
    Flexbox 布局教程
    到底vuex是什么?
    CSS3 动画 animation和@keyframes
    zabbix添加触发器Triggers
    zabbix邮件报警
    修改zabbix监控项刷新时间
  • 原文地址:https://www.cnblogs.com/ListenFly/p/3496336.html
Copyright © 2020-2023  润新知