作者:微软
Reflection提供诸如Microsoft Visual Basic.NET和JScript语言编译器使用的底层结构来实施隐性后绑定。绑定是定位与某一特定类型相对应的声明的过程。当这个过程发生在运行的时候,而不是编译的时候,它被称为后绑定。Visual Basic.NET使你可以在你的代码中使用隐性后绑定;VisualBasic.NET编译器调用helper 方法,使用Reflection获得对象类型。传递给helper 方法的参数 使适当的方法可以在运行时被调用。这些参数是调用方法(对象)的实例,被调用方法的名字(字符串),及传递给被调用方法的参数。(一个对象数组)。
在以下代码例子中, Visual Basic.NET编译器通过Reflection隐性地 来对一在编译时不知类型的对象调用方法。HelloWorld 类有一种 PrintHello 方法,可以打印出 "Hello World" 及传递给PrintHello 方法的一些文本。本例中PrintHello 方法 调用实际上是Type. InvokeMember ; Visual Basic 代码 允许PrintHello 方法被调用,仿佛 对象的类型 (helloObj)在编译时就已经知道了(前期绑定),而不是在运行时(后绑定)。
[Visual Basic]
Imports System
Module Hello
Sub Main()
' Set up variable.
Dim helloObj As Object
' Create the object.
helloObj = new HelloWorld()
' Invoke the print method as if it was early bound
' even though it's really late bound.
helloObj.PrintHello("Visual Basic Late Bound")
End Sub
End Module
自定义绑定
Reflection除了可以隐性地被编译器用于后绑定,也可以在代码中显示使用,来完成后绑定。
common language runtime 支持多种编程语言,这些语言的绑定规则互不相同。在前绑定的情况下,代码生成器能完全控制绑定。然而,在使用Reflection的后绑定中,绑定必须由自定义绑定控制。Binder类提供成员选择与调用的自定义控制。
使用自定义绑定, 您可以在运行时装载assembly,获得assembly中关于类型的信息,指明您索要的类型,并且调用方法,访问字段,或类型的属性。如果在编译时您不知道对象的类型,该技术就显得格外有用,比如,当对象类型依赖于用户输入时。以下例子中的代码显示了在HelloWorld.dll assembly 中,被动态使用Reflection调用的方法,第一个在Visual Basic.NET,第二个在C#中。
[Visual Basic]
' This class is deployed as an assembly consisting Hello World string.
Private m_helloWorld As String = "HelloWorld"
' Default public constructor.
Public Sub New()
End Sub 'New
' Print "Hello World" plus thepassed text.
Public Sub PrintHello(txt As String)
' Output to the Console.
Console.WriteLine((m_helloWorld & "" & txt))
End Sub
End Class
Imports System
Imports System.Reflection
Module VisualBasicLateHello
Sub Main()
' Set up the variables.
Dim assem as System.Reflection.Assembly
Dim obj as Object
Dim helloType as Type
Dim printMethod as MethodInfo
' Load the assembly to use.
assem = System.Reflection.Assembly.Load("HelloWorld")
' Get the type to use from the assembly.
helloType = assem.GetType("HelloWorld")
' Get the method to use from the type.
printMethod = helloType.GetMethod("PrintHello")
' Create an instance of the type.
obj = Activator.CreateInstance(helloType)
' Create an array to hold the arguments.
Dim args(1) as Object
' Set the arguments.
args(0) = "From Visual Basic Late Bound"
' Invoke the method.
printMethod.Invoke(obj, args)
End Sub
End Module
以下为C# 版:
[C#]
// This class is deployed as an assembly consisting of one DLL,
// called HelloWorld.dll.
using System;
public class HelloWorld {
// Constant Hello World string.
private const String m_helloWorld = "Hello World";
// Default public constructor.
public HelloWorld() {
}
// Print "Hello World" plus the passed text.
public void PrintHello(String txt) {
// Output to the Console.
Console.WriteLine(m_helloWorld + " " + txt);
}
}
// Illustrates reflection's late binding functionality.
// Calls the PrintHello method on a dynamically loaded
// and created instance of the HelloWorld class.
using System;
using System.Reflection;
public class CSharpLateHello {
public static void Main() {
// Load the assembly to use.
Assembly assem = Assembly.Load("HelloWorld");
// Get the type to use from the assembly.
Type helloType = assem.GetType("HelloWorld");
// Get the method to call from the type.
MethodInfo printMethod = helloType.GetMethod("PrintHello");
// Create an instance of the HelloWorld class.
Object obj = Activator.CreateInstance(helloType);
// Create the args array.
Object[] args = new Object[1];
// Set the arguments.
args[0] = "From CSharp Late Bound";
// Invoke the PrintHello method.
printMethod.Invoke(obj, args);
}
}
InvokeMember 与 CreateInstance
可以使用Type.InvokeMember来调用某类型成员。各种类的CreateInstance 方法,例如System.Activator 和 System.Reflection.Assembly,是InvokeMember的专用形式,用于生成某类型新的实例。Binder类在这些方法中,被用于重载解析和参数转换。
以下例子中的代码显示了三种可能的参数转换及成员选择的组合。在Case1中, 不需要参数转换或成员选择。在Case 2中,只需要成员选择。在Case3中, 只需要参数转换。
[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 memberselection 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. 如果无法调用,BindToMethod 返回 MethodBase 为 调用或 null。MethodBase返回值无需是match参数之一,尽管事实往往如此。
调用者 也许会想得到ByRef 参数的返回。所以,如果BindTo方法改动过参数数组,Binder 允许客户使参数数组映射回它原来的表格。为了实现这点,调用者必须确保参数顺序不变。当参数由名字传递,Binder重新整理参数组,以供调用者察看。
可用成员是指那些在类型或任何基本类型中定义的那些成员。如果指明BindingFlags.NonPublic,任何访问级别的成员都会在返回中。如果BindingFlags.NonPublic 没有被指明,binder必须执行访问规则。当指明Public或 NonPublic 绑定标志, 你必须也指明Instance 或Static 标志, 否则不会有成员返回。
如果只有一个成员与名字对应,就不需要回调,也就完成到这个方法的绑定。Case 1 中的代码例子表明了这一点:只有一个可用的PrintBob 方法, 所以,不需要回调。
如在可用集中,有多于一个成员。所有这些方法被传递给BindTo方法, 再由它选择适当的方法,并且返回。在 Case 2 中的代码例子中,有两种叫做PrintValue的方法。合适的方法取决于对BindToMethod调用。
ChangeType 执行参数转换, 它把实际参数转变为选定方法的参数类型。即使类型已经完美匹配,ChangeType也会针对每个参数被调用。
在 Case 3 中的代码例子中, 值为"5.5"的String类型的一个实际参数以正式参数Double类型被传递给方法。要想调用成功,字符串值"5.5"必须被转变为一个double值。ChangeType 执行了这种转变。
ChangeType 仅执行无损失转换, 如下表所示:
Source Type Target Type
Any type Its base type
Any type Interface it implements
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
Non-reference type Reference type
Type类有Get方法,可使用Binder类型的参数的来解析对某成员的引用。Type.GetConstructor,Type. GetMethod , 和 Type.GetProperty 通过提供某成员的签名信息来查找该成员。它们调用Binder.SelectMethod和Binder.SelectProperty 以选择适合方法的签名信息。