动态加载和使用类型
2012-10-26 16:54
405 查看
http://msdn.microsoft.com/zh-cn/library/k3a58006%28v=vs.90%29.aspx
反射提供语言编译器(如 Microsoft Visual Basic 2005 和 JScript)用于实现隐式后期绑定的基础结构。绑定是查找与唯一指定的类型相对应的声明(即实现)的过程。如果此过程是在运行时而不是在编译时发生,则称其为“后期绑定”。利用 Visual Basic 2005,可以在代码中使用隐式后期绑定;Visual Basic 编译器会调用一个帮助器方法,该方法使用反射来获取对象类型。传递给帮助器方法的参数有助于在运行时调用正确的方法。这些参数包括:对其调用方法的实例(对象),被调用方法的名称(字符串),以及传递给被调用方法的参数(对象数组)。
在下面的示例中,Visual Basic 编译器使用反射隐式地对其类型在编译时未知的对象调用方法。HelloWorld 类具有一个
PrintHello 方法,它输出与传递给 PrintHello 方法的某些文本串联的“Hello World”。在此示例中调用的
PrintHello 方法实际上是 Type.InvokeMember;Visual Basic 代码允许按照对象 (helloObj) 的类型在编译时已知(早期绑定)而不是在运行时已知(后期绑定)的方式来调用
PrintHello 方法。
自定义绑定
除了由编译器隐式地用来进行后期绑定之外,反射还可以在代码中显式地用来完成后期绑定。
公共语言运行库支持多种编程语言,但这些语言的绑定规则各不相同。在早期绑定的情况下,代码生成器可以完全控制此绑定。但是,当通过反射进行后期绑定时,必须用自定义绑定来控制绑定。Binder
类提供了对成员选择和调用的自定义控制。
利用自定义绑定,您可以在运行时加载程序集,获取有关该程序集中类型的信息,然后对该类型调用方法或访问该类型的字段或属性。如果您在编译时(例如当对象类型依赖于用户输入时)不知道对象的类型,就可以使用这种方法。
下面的示例说明不提供参数类型转换的简单的自定义联编程序。Simple_Type.dll 的代码位于示例主体之前。确保生成
Simple_Type.dll,然后在生成时在项目中包括对它的引用。
C#
VB
System.Activator 和
System.Reflection.Assembly)的 CreateInstance 方法是 InvokeMember 的特殊形式,它们可新建特定类型的实例。Binder 类用于在这些方法中进行重载决策和参数强制。
下面的示例显示参数强制(类型转换)和成员选择的三种可能的组合。在第 1 种情况中,不需要任何参数强制或成员选择。在第 2 种情况中,只需要成员选择。在第 3 种情况中,只需要参数强制。
C#
当多个成员具有相同的名称时,将需要重载决策。Binder.BindToMethod 和
Binder.BindToField 方法用于解析与单个成员的绑定。Binder.BindToMethod 还通过
get 和 set 属性访问器提供了属性解析。
BindToMethod 返回要调用的 MethodBase;如果无法进行这种调用,则返回 null 引用(在 Visual Basic 中为
Nothing)。虽然 MethodBase 返回值通常是 match 参数中所包含的值之一,但它并不必如此。
当存在 ByRef 参数时,调用方可能需要取回这些参数。因此,如果 BindToMethod 已经操作参数数组,Binder
会允许客户端将参数数组映射回它的初始形式。为了实现这一目的,必须向调用方保证参数的顺序不会改变。当按名称传递参数时,Binder
将重新排列参数数组,这就是调用方所见的参数。有关更多信息,请参见 Binder.ReorderArgumentArray。
可用成员集包括在类型和任何基类型中定义的成员。如果指定
BindingFlags.NonPublic,将返回该成员集中具有任何可访问性的成员。如果未指定 BindingFlags.NonPublic,联编程序就必须强制可访问性规则。当指定
Public 或 NonPublic 绑定标志时,还必须指定 Instance 或
Static 绑定标志,否则不会返回任何成员。
如果只有一个成员具有给定名称,则不必进行回调,而在该方法上进行绑定。代码示例的第 1 种情况说明了这一点:只有一个 PrintBob 方法可用,因此不需要进行回调。
如果可用集中有多个成员,所有这些方法都将传递给 BindToMethod,它将选择正确的方法并将其返回。在代码示例的第 2 种情况下,有两个名为
PrintValue 的方法。对 BindToMethod 的调用将选择正确的方法。
ChangeType 执行参数强制转换(类型转换),以便将实参转换为选定方法的形参的类型。即使类型完全匹配,也会为每个参数调用
ChangeType。
在代码示例的第 3 种情况下,将类型为 String 值为“5.5”的实参传递给了具有类型为 Double 的形参的方法。要使调用成功,必须将字符串值“5.5”转换为 double 值。ChangeType 会执行此转换。
ChangeType 仅执行无损或扩大强制,如下表所示。
Type 类具有
Get 方法,这些方法使用 Binder 类型的参数来解析对特定成员的引用。Type.GetConstructor、Type.GetMethod
和 Type.GetProperty 通过为当前类型的特定成员提供签名信息来搜索该成员。对
Binder.SelectMethod 和
Binder.SelectProperty 进行回调以选择相应方法的给定签名信息。
反射提供语言编译器(如 Microsoft Visual Basic 2005 和 JScript)用于实现隐式后期绑定的基础结构。绑定是查找与唯一指定的类型相对应的声明(即实现)的过程。如果此过程是在运行时而不是在编译时发生,则称其为“后期绑定”。利用 Visual Basic 2005,可以在代码中使用隐式后期绑定;Visual Basic 编译器会调用一个帮助器方法,该方法使用反射来获取对象类型。传递给帮助器方法的参数有助于在运行时调用正确的方法。这些参数包括:对其调用方法的实例(对象),被调用方法的名称(字符串),以及传递给被调用方法的参数(对象数组)。
在下面的示例中,Visual Basic 编译器使用反射隐式地对其类型在编译时未知的对象调用方法。HelloWorld 类具有一个
PrintHello 方法,它输出与传递给 PrintHello 方法的某些文本串联的“Hello World”。在此示例中调用的
PrintHello 方法实际上是 Type.InvokeMember;Visual Basic 代码允许按照对象 (helloObj) 的类型在编译时已知(早期绑定)而不是在运行时已知(后期绑定)的方式来调用
PrintHello 方法。
Imports System Module Hello Sub Main() ' Sets up the variable. Dim helloObj As Object ' Creates the object. helloObj = new HelloWorld() ' Invokes the print method as if it was early bound ' even though it is really late bound. helloObj.PrintHello("Visual Basic Late Bound") End Sub End Module
自定义绑定
除了由编译器隐式地用来进行后期绑定之外,反射还可以在代码中显式地用来完成后期绑定。
公共语言运行库支持多种编程语言,但这些语言的绑定规则各不相同。在早期绑定的情况下,代码生成器可以完全控制此绑定。但是,当通过反射进行后期绑定时,必须用自定义绑定来控制绑定。Binder
类提供了对成员选择和调用的自定义控制。
利用自定义绑定,您可以在运行时加载程序集,获取有关该程序集中类型的信息,然后对该类型调用方法或访问该类型的字段或属性。如果您在编译时(例如当对象类型依赖于用户输入时)不知道对象的类型,就可以使用这种方法。
下面的示例说明不提供参数类型转换的简单的自定义联编程序。Simple_Type.dll 的代码位于示例主体之前。确保生成
Simple_Type.dll,然后在生成时在项目中包括对它的引用。
C#
VB
// Code for building SimpleType.dll. using System; namespace Simple_Type { public class MySimpleClass { public void MyMethod(string str, int i) { Console.WriteLine("MyMethod parameters: {0}, {1}", str, i); } public void MyMethod(string str, int i, int j) { Console.WriteLine("MyMethod parameters: {0}, {1}, {2}", str, i, j); } } } using System; using System.Reflection; using System.Globalization; using Simple_Type; namespace Custom_Binder { class MyMainClass { static void Main() { // Get the type of MySimpleClass. Type myType = typeof(MySimpleClass); // Get an instance of MySimpleClass. MySimpleClass myInstance = new MySimpleClass(); MyCustomBinder myCustomBinder = new MyCustomBinder(); // Get the method information for the particular overload // being sought. MethodInfo myMethod = myType.GetMethod("MyMethod", BindingFlags.Public | BindingFlags.Instance, myCustomBinder, new Type[] {typeof(string), typeof(int)}, null); Console.WriteLine(myMethod.ToString()); // Invoke the overload. myType.InvokeMember("MyMethod", BindingFlags.InvokeMethod, myCustomBinder, myInstance, new Object[] {"Testing...", (int)32}); } } // **************************************************** // A simple custom binder that provides no // argument type conversion. // **************************************************** class MyCustomBinder : Binder { public override MethodBase BindToMethod( BindingFlags bindingAttr, MethodBase[] match, ref object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] names, out object state) { if(match == null) throw new ArgumentNullException("match"); // Arguments are not being reordered. state = null; // Find a parameter match and return the first method with // parameters that match the request. foreach(MethodBase mb in match) { ParameterInfo[] parameters = mb.GetParameters(); if(ParametersMatch(parameters, args)) return mb; } return null; } public override FieldInfo BindToField(BindingFlags bindingAttr, FieldInfo[] match, object value, CultureInfo culture) { if(match == null) throw new ArgumentNullException("match"); foreach(FieldInfo fi in match) { if(fi.GetType() == value.GetType()) return fi; } return null; } public override MethodBase SelectMethod( BindingFlags bindingAttr, MethodBase[] match, Type[] types, ParameterModifier[] modifiers) { if(match == null) throw new ArgumentNullException("match"); // Find a parameter match and return the first method with // parameters that match the request. foreach(MethodBase mb in match) { ParameterInfo[] parameters = mb.GetParameters(); if(ParametersMatch(parameters, types)) return mb; } return null; } public override PropertyInfo SelectProperty( BindingFlags bindingAttr, PropertyInfo[] match, Type returnType, Type[] indexes, ParameterModifier[] modifiers) { if(match == null) throw new ArgumentNullException("match"); foreach(PropertyInfo pi in match) { if(pi.GetType() == returnType && ParametersMatch(pi.GetIndexParameters(), indexes)) return pi; } return null; } public override object ChangeType( object value, Type myChangeType, CultureInfo culture) { try { object newType; newType = Convert.ChangeType(value, myChangeType); return newType; } // Throw an InvalidCastException if the conversion cannot // be done by the Convert.ChangeType method. catch(InvalidCastException) { return null; } } public override void ReorderArgumentArray(ref object[] args, object state) { // No operation is needed here because BindToMethod does not // reorder the args array. The most common implementation // of this method is shown below. // ((BinderState)state).args.CopyTo(args, 0); } // Returns true only if the type of each object in a matches // the type of each corresponding object in b. private bool ParametersMatch(ParameterInfo[] a, object[] b) { if(a.Length != b.Length) return false; for(int i = 0; i < a.Length; i++) { if(a[i].ParameterType != b[i].GetType()) return false; } return true; } // Returns true only if the type of each object in a matches // the type of each corresponding entry in b. private bool ParametersMatch(ParameterInfo[] a, Type[] b) { if(a.Length != b.Length) return false; for(int i = 0; i < a.Length; i++) { if(a[i].ParameterType != b[i]) return false; } return true; } } }
InvokeMember 和 CreateInstance
使用 Type.InvokeMember 可调用类型的成员。各个类(如System.Activator 和
System.Reflection.Assembly)的 CreateInstance 方法是 InvokeMember 的特殊形式,它们可新建特定类型的实例。Binder 类用于在这些方法中进行重载决策和参数强制。
下面的示例显示参数强制(类型转换)和成员选择的三种可能的组合。在第 1 种情况中,不需要任何参数强制或成员选择。在第 2 种情况中,只需要成员选择。在第 3 种情况中,只需要参数强制。
C#
public class CustomBinderDriver { public static void Main (string[] arguments) { Type t = typeof (CustomBinderDriver); CustomBinder binder = new CustomBinder(); BindingFlags flags = BindingFlags.InvokeMethod|BindingFlags.Instance| BindingFlags.Public|BindingFlags.Static; // Case 1. Neither argument coercion nor member selection is needed. args = new Object[] {}; t.InvokeMember ("PrintBob", flags, binder, null, args); // Case 2. Only member selection is needed. args = new Object[] {42}; t.InvokeMember ("PrintValue", flags, binder, null, args); // Case 3. Only argument coercion is needed. args = new Object[] {"5.5"}; t.InvokeMember ("PrintNumber", flags, binder, null, args); } public static void PrintBob () { Console.WriteLine ("PrintBob"); } public static void PrintValue (long value) { Console.WriteLine ("PrintValue ({0})", value); } public static void PrintValue (String value) { Console.WriteLine ("PrintValue\"{0}\")", value); } public static void PrintNumber (double value) { Console.WriteLine ("PrintNumber ({0})", value); } }
当多个成员具有相同的名称时,将需要重载决策。Binder.BindToMethod 和
Binder.BindToField 方法用于解析与单个成员的绑定。Binder.BindToMethod 还通过
get 和 set 属性访问器提供了属性解析。
BindToMethod 返回要调用的 MethodBase;如果无法进行这种调用,则返回 null 引用(在 Visual Basic 中为
Nothing)。虽然 MethodBase 返回值通常是 match 参数中所包含的值之一,但它并不必如此。
当存在 ByRef 参数时,调用方可能需要取回这些参数。因此,如果 BindToMethod 已经操作参数数组,Binder
会允许客户端将参数数组映射回它的初始形式。为了实现这一目的,必须向调用方保证参数的顺序不会改变。当按名称传递参数时,Binder
将重新排列参数数组,这就是调用方所见的参数。有关更多信息,请参见 Binder.ReorderArgumentArray。
可用成员集包括在类型和任何基类型中定义的成员。如果指定
BindingFlags.NonPublic,将返回该成员集中具有任何可访问性的成员。如果未指定 BindingFlags.NonPublic,联编程序就必须强制可访问性规则。当指定
Public 或 NonPublic 绑定标志时,还必须指定 Instance 或
Static 绑定标志,否则不会返回任何成员。
如果只有一个成员具有给定名称,则不必进行回调,而在该方法上进行绑定。代码示例的第 1 种情况说明了这一点:只有一个 PrintBob 方法可用,因此不需要进行回调。
如果可用集中有多个成员,所有这些方法都将传递给 BindToMethod,它将选择正确的方法并将其返回。在代码示例的第 2 种情况下,有两个名为
PrintValue 的方法。对 BindToMethod 的调用将选择正确的方法。
ChangeType 执行参数强制转换(类型转换),以便将实参转换为选定方法的形参的类型。即使类型完全匹配,也会为每个参数调用
ChangeType。
在代码示例的第 3 种情况下,将类型为 String 值为“5.5”的实参传递给了具有类型为 Double 的形参的方法。要使调用成功,必须将字符串值“5.5”转换为 double 值。ChangeType 会执行此转换。
ChangeType 仅执行无损或扩大强制,如下表所示。
源类型 | 目标类型 |
---|---|
任何类型 | 它的基类型 |
任何类型 | 它所实现的接口 |
Char | UInt16、UInt32、Int32、UInt64、Int64、Single、Double |
Byte | Char、UInt16、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 |
非引用类型 | 引用类型 |
Get 方法,这些方法使用 Binder 类型的参数来解析对特定成员的引用。Type.GetConstructor、Type.GetMethod
和 Type.GetProperty 通过为当前类型的特定成员提供签名信息来搜索该成员。对
Binder.SelectMethod 和
Binder.SelectProperty 进行回调以选择相应方法的给定签名信息。
相关文章推荐
- 反射-动态加载和使用类型
- 动态加载和使用类型
- java 中能否使用 动态加载的类(Class.forName) 来做类型转换?
- EF使用动态类型
- 遍历ZIP(JAR)文件,动态加载,找出其中指定类型的类
- android Fragment动态加载的使用详解
- Java Class类的使用 和 动态加载类
- Android 使用动态加载框架DL进行插件化开发
- ABAP--动态创建类型和变量的使用程序样例
- Android插件化(二):使用DexClassLoader动态加载assets中的apk
- Android ListView 中Adapter的使用及listView的动态加载
- 动态加载plist文件和SDK接口使用方式
- hive原生和复合类型的数据加载和使用
- asp.net 中使用iframe动态加载页面
- 使用泛型解决需要动态返回类型的问题
- 使用jQuery动态加载js脚本文件的方法
- JavaScript学习总结(十九)——使用js加载器动态加载外部Javascript文件
- Android 使用动态加载框架DL进行插件化开发
- android 动态加载框架 快速使用
- android: 静态XML和动态加载XML混合使用,以及重写Layout控件