利用反射来动态创建实例和调用方法

    技术2022-05-11  48

                                      利用反射来动态创建实例和调用方法         .NET的一个强大功能是它可以通过一种称为反射(reflection)的过程访问应用程序的元数据。简单地说,反射就是运行时查询类型信息的能力。.NET Reflection API 实际上是在System.Reflection命名空间中定义的一组类。这些类使你能够按逻辑方式查看配件和类型信息。我在这里并不对反射的基本原理做讲解,只是针对一些反射的基本用法做一下介绍。关于反射的定义还有一些理论上的东西请参见网上其它的文章或是msdn。         这里我只讲三个方面的应用:一是用反射来创建实例,二是用反射调用实例的方法,三是用反射调用实例的属性。这三个是反射的最基本用法和用途,其它的用法比如调用实例的特性、委托等等,与这三个差不多,请读者自己学习。         学习反射要具备很多基本知识,这里并不准备讲这些东西,只是开门见山的讲其三个用法。         在此之前,我想说说Type这个类,不感兴趣的读者可以不看这一段。反射的核心是抽象的System.Type这个类。这个类是所有反射操作的根并且代表系统中的每个类型。这个类是访问元数据的主要方式,它作为通向Reflection API的通道。Type类的成员用于获取关于类型声明的信息。这些信息包括为类型定义的所有构造器、方法、字段、特性和事件,以及类所在的模块和配件。Type对象可以代表以下任何类型:类、值类型、数组、接口、指针和枚举。         下面的程序是一个名为ReflectionExample的简单项目,里面包括MainClass.cs和InstanceClass.cs两个文件,MainClass里面利用字符串来创建InstanceClass类并且调用InstanceClass中的属性和方法,其中包括调用缺省构造函数和调用重载构造函数、调用无参数的成员函数和带参数的成员函数四个方法,还有一个ReturnString属性,程序如下。 InstanceClass.cs文件 using System;   namespace ReflectionExample {         ///<summary>         /// InstanceClass 的摘要描述。         ///</summary>         public class InstanceClass         {                 private string returnString;                 public string ReturnString                 {                         get { return returnString ; }                         set { returnString = value ;}                 }                   public InstanceClass()                 {                         //                         // TODO: 在此加入建构函式的程序代码                         //                         this.returnString = "Creat object without Parameter";                 }                   public InstanceClass( string str )                 {                         this.returnString = str;                 }                   public void FunctionWithoutParameter()                 {                         Console.WriteLine( "Function Without Parameter" );                 }                   public void FunctionWithParameter( string str )                 {                         Console.WriteLine( str );                 }         } } MainClass.cs文件 using System; using System.Reflection;   namespace ReflectionExample {         ///<summary>         /// Class1 的摘要描述。         ///</summary>         class MainClass         {                 private Type type = null;                 ///<summary>                 ///应用程序的主进入点。                 ///</summary>                 [STAThread]                 static void Main(string[] args)                 {                         //                         // TODO: 在此加入启动应用程序的程序代码                         //                         Type type = typeof(InstanceClass);//利用typeof来获得InstanceClass类型。                         MainClass mainClass = new MainClass(type);//初始化MainClass                         mainClass.GetObjectMethod();//调用无参数的函数                         mainClass.GetObjectMethod( "Function With Parameter" );//调用带参数的函数                         mainClass.GetObjectProperty();//调用缺省构造函数来初始化InstanceClass中的ReturnString属性                         mainClass.GetObjectProperty("Creat object with Parameter");//调用重载构造函数来初始化InstanceClass中的ReturnString属性                 }                   public MainClass( Type type )                 {                         this.type = type ;                 }                   public void GetObjectMethod()                 {                         try                         {                                 //Activator.CreateInstance()调用类的缺省构造函数来创建实例                                 object o = Activator.CreateInstance( type );                                 //利用MethodInfo类来获得从指定类中的成员函数                                 MethodInfo mi = type.GetMethod("FunctionWithoutParameter" );                                 //调用此成员函数                                 mi.Invoke( o , null );                         }                         catch( Exception ex )                         {                                 Console.WriteLine( ex.Message );                         }                         finally                         {                                 Console.ReadLine();                         }                 }                   public void GetObjectMethod( string str )                 {                         try                         {                                 object o = Activator.CreateInstance( type );                                 //利用MethodInfo类来获得从指定类中符合条件的成员函数                                 MethodInfo mi = type.GetMethod( "FunctionWithParameter" ,                                              BindingFlags.Public|BindingFlags.Instance , null , new                                                 Type[]{ typeof(string) } , null );                                 mi.Invoke( o , new object[]{ str } );                         }                         catch( Exception ex )                         {                                 Console.WriteLine( ex.Message );                         }                         finally                         {                                 Console.ReadLine();                         }                 }                   public void GetObjectProperty( )                 {                         try                         {                                 object o = Activator.CreateInstance( type );                                 //利用PropertyInfo类来获得从指定类中的属性                                 PropertyInfo pi = type.GetProperty( "ReturnString" , typeof(string) );                                 //打印属性值                                 Console.WriteLine( pi.GetValue( o , null ) );                         }                         catch( Exception ex )                         {                                 Console.WriteLine( ex.ToString() );                         }                         finally                         {                                 Console.ReadLine();                         }                 }                   public void GetObjectProperty( string str )                 {                         try                         {                                 //Activator.CreateInstance()调用类的重载构造函数来创建实例                                 object o = Activator.CreateInstance( type , new object[]{ "Creat                                    object with Parameter" } );                                 PropertyInfo pi = type.GetProperty( "ReturnString" ,                                                      typeof(string) );                                                        Console.WriteLine( pi.GetValue( o , null ) );                         }                         catch( Exception ex )                         {                                 Console.WriteLine( ex.ToString() );                         }                         finally                         {                                 Console.ReadLine();                         }                 }         } }         以上只是对反射基本用法的一个小例子,没有展开讲,不过我们可以看出其实基本思路已经很清晰了,用Type取得实例类型后,用Activator.CreateInstance()创建实例,再用MethodInfo来调用类中的方法,用PropertyInfo来调用类中的属性,调用事件和参数等方法也是类似,请参见下面的说明。 1.      使用Module了解包含模块的程序集以及模块中的类等,还可以获取在模块上定义的所有全局方法或其它特定的非全局方法。 2.使用ConstructorInfo了解构造函数的名称、参数、访问修饰符(如public 或private)和实现详细信息(如abstract或virtual)等。使用Type的GetConstructors或GetConstructor方法来调用特定的构造函数。 3.使用MethodInfo了解方法的名称、返回类型、参数、访问修饰符(如public 或private)和实现详细信息(如abstract或virtual)等。使用Type的GetMethods或GetMethod方法来调用特定的方法。 4.使用FieldInfo了解字段的名称、访问修饰符(如public或private)和实现详细信息(如static)等,并获取或设置字段值。5.使用EventInfo了解事件的名称、事件处理程序数据类型、自定义属性、声明类型和反射类型等,添加或移除事件处理程序。 6.使用PropertyInfo了解属性的名称、数据类型、声明类型、反射类型和只读或可写状态等,获取或设置属性值。 7.使用ParameterInfo了解参数的名称、数据类型、是输入参数还是输出参数,以及参数在方法签名中的位置等。           上面的程序很简单,唯一要说的就是 MethodInfo mi = type.GetMethod( "FunctionWithParameter" ,                                              BindingFlags.Public        |BindingFlags.Instance , null , new                                                 Type[]{ typeof(string) } , null ); 这个GetMethod中的参数说明了,如下: public virtual MethodInfo GetMethod(    string name,    BindingFlags bindingAttr,    Binder binder,    Type[] types,    ParameterModifier[] modifiers ); 参数 name 包含要获取的方法名称的 String。 bindingAttr 一个位屏蔽,由一个或多个指定搜索执行方式的 BindingFlags 组成。 binder 一个 Binder 对象,该对象定义一组属性并启用绑定,而绑定可能涉及选择重载方法、强制参数类型和通过反射调用成员。 types 表示此方法要获取的参数的个数、顺序和类型的 Type 对象数组。 获取不使用参数的方法的 Type 类型的空数组(即 Type[] types = new Type[0])。 modifiers ParameterModifier 对象数组,表示与 types 数组中的相应元素关联的属性。默认的联编程序不处理此参数。 返回值 表示符合指定要求的方法的 MethodInfo 对象或是NULL;   其中bindingAttr中的BindingFlags定义如下。   成员名称 说明 CreateInstance 受 .NET Framework 精简版的支持。 指定“反射”应该创建指定类型的实例。调用与给定参数匹配的构造函数。忽略提供的成员名。如果未指定查找类型,将应用 (Instance |Public)。调用类型初始值设定项是不可能的。 512 DeclaredOnly 受 .NET Framework 精简版的支持。 指定只应考虑在所提供类型的层次结构级别上声明的成员。不考虑继承成员。 2 Default 受 .NET Framework 精简版的支持。 不指定绑定标志。 0 ExactBinding 受 .NET Framework 精简版的支持。 指定提供参数的类型必须与对应形参的类型完全匹配。如果调用方提供一个非空 Binder 对象,则“反射”将引发异常,因为这意味着调用方正在提供的 BindToXXX 实现将选取适当的方法。 “反射”建立通用类型系统的访问规则模型。例如,如果调用方在相同的程序集内,则它不需要内部成员的特殊权限。否则,调用方需要 ReflectionPermission。这与保护成员、私有成员等成员的查找是一致的。 一般原则是, ChangeType 应只执行永远不会丢失数据的扩展强制。扩展强制的一个例子是将 32 位有符号整数值强制为 64 位有符号整数值。这与窄缩强制不同,后者可能丢失数据。窄缩强制的一个例子是将 64 位有符号整数强制为 32 位有符号整数。 默认联编程序忽略此标志,而自定义联编程序可以实现此标志的语义。 65536 FlattenHierarchy 受 .NET Framework 精简版的支持。 指定应返回该层次结构以上的静态成员。静态成员包括字段、方法、事件和属性。不返回嵌套类型。 64 GetField 受 .NET Framework 精简版的支持。 指定应返回指定字段的值。 1024 GetProperty 受 .NET Framework 精简版的支持。 指定应返回指定属性的值。 4096 IgnoreCase 受 .NET Framework 精简版的支持。 指定当绑定时不应考虑成员名的大小写。 1 IgnoreReturn 受 .NET Framework 精简版的支持。 在 COM interop 中用于指定可以忽略成员的返回值。 16777216 Instance 受 .NET Framework 精简版的支持。 指定实例成员将包括在搜索中。 4 InvokeMethod 受 .NET Framework 精简版的支持。 指定要调用一个方法。这可能不是构造函数或类型初始值设定项。 256 NonPublic 受 .NET Framework 精简版的支持。 指定非公共成员将包括在搜索中。 32 OptionalParamBinding 受 .NET Framework 精简版的支持。 返回其参数计数与提供参数的数目匹配的成员集。此绑定标志用于所带参数具有默认值的方法和带变量参数 (varargs) 的方法。此标志应只与 Type.InvokeMember 一起使用。 具有默认值的参数仅用在省略尾部参数的调用中。它们必须是最后的参数。 262144 Public 受 .NET Framework 精简版的支持。 指定公共成员将包括在搜索中。 16 PutDispProperty 受 .NET Framework 精简版的支持。 指定应调用 COM 对象的 PROPPUT 成员。PROPPUT 指定使用值的属性设置函数。如果属性同时具有 PROPPUT 和 PROPPUTREF,而且需要区分调用哪一个,请使用 PutDispProperty。 16384 PutRefDispProperty 受 .NET Framework 精简版的支持。 指定应调用 COM 对象的 PROPPUTREF 成员。PROPPUTREF 指定使用引用而不是值的属性设置函数。如果属性同时具有 PROPPUT 和 PROPPUTREF,而且需要区分调用哪一个,请使用 PutRefDispProperty。 32768 SetField 受 .NET Framework 精简版的支持。 指定应设置指定字段的值。 2048 SetProperty 受 .NET Framework 精简版的支持。 指定应设置指定属性的值。对于 COM 属性,指定此绑定标志与指定 PutDispProperty 和 PutRefDispProperty 是等效的。 8192 Static 受 .NET Framework 精简版的支持。 指定静态成员将包括在搜索中。 8 SuppressChangeType 受 .NET Framework 精简版的支持。 未实现。 131072           以上只是对反射作了一个简单的介绍,大家可以按照上面的程序照猫画虎,写出自己需要的反射程序。也许你会有疑问,不是说反射可以在程序运行时动态创建类型吗?上面的例子虽然也是动态创建的,可以还是要在知道类的名字和类中方法名的情况下才可以动态的调用类中的函数,怎样实现程序动态加载dll并且执行里面的函数呢?这还需要接口和抽象类的相关知识,请看我关于接口和抽象类的这篇文章。 如果你对接口和抽象类已经很清楚了,那么可以直接看下面这篇文章, 里面有具体怎么利用反射和接口来实现动态加载dll的例子。 (其实上面这两篇文章我已经写完了,但总是觉得有些地方写得很肤浅,有些地方写得不明不白,其实反射本身就是很灵活的东西,本人水平有限,所以决定暂时先不发布上面两篇,等我对反射的理解更深时再发布。)    

    最新回复(0)