作者:微软
Reflection提供诸如MicrosoftVisualBasic.NET和JScript语言编译器使用的底层结构来实施隐性后绑定。绑定是定位与某一特定类型相对应的声明的过程。当这个过程发生在运行的时候,而不是编译的时候,它被称为后绑定。VisualBasic.NET使你可以在你的代码中使用隐性后绑定;VisualBasic.NET编译器调用helper方法,使用Reflection获得对象类型。传递给helper方法的参数使适当的方法可以在运行时被调用。这些参数是调用方法(对象)的实例,被调用方法的名字(字符串),及传递给被调用方法的参数。(一个对象数组)。
在以下代码例子中,VisualBasic.NET编译器通过Reflection隐性地来对一在编译时不知类型的对象调用方法。HelloWorld类有一种PrintHello方法,可以打印出"HelloWorld"及传递给PrintHello方法的一些文本。本例中PrintHello方法调用实际上是Type.InvokeMember;VisualBasic代码允许PrintHello方法被调用,仿佛对象的类型(helloObj)在编译时就已经知道了(前期绑定),而不是在运行时(后绑定)。
[VisualBasic]
ImportsSystem
ModuleHello
SubMain()
'Setupvariable.
DimhelloObjAsObject
'Createtheobject.
helloObj=newHelloWorld()
'Invoketheprintmethodasifitwasearlybound
'eventhoughit'sreallylatebound.
helloObj.PrintHello("VisualBasicLateBound")
EndSub
EndModule
自定义绑定
Reflection除了可以隐性地被编译器用于后绑定,也可以在代码中显示使用,来完成后绑定。
commonlanguageruntime支持多种编程语言,这些语言的绑定规则互不相同。在前绑定的情况下,代码生成器能完全控制绑定。然而,在使用Reflection的后绑定中,绑定必须由自定义绑定控制。Binder类提供成员选择与调用的自定义控制。
使用自定义绑定,您可以在运行时装载assembly,获得assembly中关于类型的信息,指明您索要的类型,并且调用方法,访问字段,或类型的属性。如果在编译时您不知道对象的类型,该技术就显得格外有用,比如,当对象类型依赖于用户输入时。以下例子中的代码显示了在HelloWorld.dllassembly中,被动态使用Reflection调用的方法,第一个在VisualBasic.NET,第二个在C#中。
[VisualBasic]
'ThisclassisdeployedasanassemblyconsistingHelloWorldstring.
Privatem_helloWorldAsString="HelloWorld"
'Defaultpublicconstructor.
PublicSubNew()
EndSub'New
'Print"HelloWorld"plusthepassedtext.
PublicSubPrintHello(txtAsString)
'OutputtotheConsole.
Console.WriteLine((m_helloWorld&""&txt))
EndSub
EndClass
ImportsSystem
ImportsSystem.Reflection
ModuleVisualBasicLateHello
SubMain()
'Setupthevariables.
DimassemasSystem.Reflection.Assembly
DimobjasObject
DimhelloTypeasType
DimprintMethodasMethodInfo
'Loadtheassemblytouse.
assem=System.Reflection.Assembly.Load("HelloWorld")
'Getthetypetousefromtheassembly.
helloType=assem.GetType("HelloWorld")
'Getthemethodtousefromthetype.
printMethod=helloType.GetMethod("PrintHello")
'Createaninstanceofthetype.
obj=Activator.CreateInstance(helloType)
'Createanarraytoholdthearguments.
Dimargs(1)asObject
'Setthearguments.
args(0)="FromVisualBasicLateBound"
'Invokethemethod.
printMethod.Invoke(obj,args)
EndSub
EndModule
以下为C#版:
[C#]
//ThisclassisdeployedasanassemblyconsistingofoneDLL,
//calledHelloWorld.dll.
usingSystem;
publicclassHelloWorld{
//ConstantHelloWorldstring.
privateconstStringm_helloWorld="HelloWorld";
//Defaultpublicconstructor.
publicHelloWorld(){
}
//Print"HelloWorld"plusthepassedtext.
publicvoidPrintHello(Stringtxt){
//OutputtotheConsole.
Console.WriteLine(m_helloWorld+""+txt);
}
}
//Illustratesreflection'slatebindingfunctionality.
//CallsthePrintHellomethodonadynamicallyloaded
//andcreatedinstanceoftheHelloWorldclass.
usingSystem;
usingSystem.Reflection;
publicclassCSharpLateHello{
publicstaticvoidMain(){
//Loadtheassemblytouse.
Assemblyassem=Assembly.Load("HelloWorld");
//Getthetypetousefromtheassembly.
TypehelloType=assem.GetType("HelloWorld");
//Getthemethodtocallfromthetype.
MethodInfoprintMethod=helloType.GetMethod("PrintHello");
//CreateaninstanceoftheHelloWorldclass.
Objectobj=Activator.CreateInstance(helloType);
//Createtheargsarray.
Object[]args=newObject[1];
//Setthearguments.
args[0]="FromCSharpLateBound";
//InvokethePrintHellomethod.
printMethod.Invoke(obj,args);
}
}
InvokeMember与CreateInstance
可以使用Type.InvokeMember来调用某类型成员。各种类的CreateInstance方法,例如System.Activator和System.Reflection.Assembly,是InvokeMember的专用形式,用于生成某类型新的实例。Binder类在这些方法中,被用于重载解析和参数转换。
以下例子中的代码显示了三种可能的参数转换及成员选择的组合。在Case1中,不需要参数转换或成员选择。在Case2中,只需要成员选择。在Case3中,只需要参数转换。
[C#]
publicclassCustomBinderDriver
{
publicstaticvoidMain(string[]arguments)
{
Typet=typeof(CustomBinderDriver);
CustomBinderbinder=newCustomBinder();
BindingFlagsflags=BindingFlags.InvokeMethod|BindingFlags.Instance|
BindingFlags.Public|BindingFlags.Static;
//Case1.Neitherargumentcoercionnormemberselectionisneeded.
args=newObject[]{};
t.InvokeMember("PrintBob",flags,binder,null,args);
//Case2.Onlymemberselectionisneeded.
args=newObject[]{42};
t.InvokeMember("PrintValue",flags,binder,null,args);
//Case3.Onlyargumentcoercionisneeded.
args=newObject[]{"5.5"};
t.InvokeMember("PrintNumber",flags,binder,null,args);
}
publicstaticvoidPrintBob()
{
Console.WriteLine("PrintBob");
}
publicstaticvoidPrintValue(longvalue)
{
Console.WriteLine("PrintValue({0})",value);
}
publicstaticvoidPrintValue(Stringvalue)
{
Console.WriteLine("PrintValue\"{0}\")",value);
}
publicstaticvoidPrintNumber(doublevalue)
{
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标志,否则不会有成员返回。
如果只有一个成员与名字对应,就不需要回调,也就完成到这个方法的绑定。Case1中的代码例子表明了这一点:只有一个可用的PrintBob方法,所以,不需要回调。
如在可用集中,有多于一个成员。所有这些方法被传递给BindTo方法,再由它选择适当的方法,并且返回。在Case2中的代码例子中,有两种叫做PrintValue的方法。合适的方法取决于对BindToMethod调用。
ChangeType执行参数转换,它把实际参数转变为选定方法的参数类型。即使类型已经完美匹配,ChangeType也会针对每个参数被调用。
在Case3中的代码例子中,值为"5.5"的String类型的一个实际参数以正式参数Double类型被传递给方法。要想调用成功,字符串值"5.5"必须被转变为一个double值。ChangeType执行了这种转变。
ChangeType仅执行无损失转换,如下表所示:
SourceTypeTargetType
AnytypeItsbasetype
AnytypeInterfaceitimplements
CharUInt16,UInt32,Int32,UInt64,Int64,Single,Double
ByteChar,UInt16,Int16,UInt32,Int32,UInt64,Int64,Single,Double
SByteInt16,Int32,Int64,Single,Double
UInt16UInt32,Int32,UInt64,Int64,Single,Double
Int16Int32,Int64,Single,Double
UInt32UInt64,Int64,Single,Double
Int32Int64,Single,Double
UInt64Single,Double
Int64Single,Double
SingleDouble
Non-referencetypeReferencetype
Type类有Get方法,可使用Binder类型的参数的来解析对某成员的引用。Type.GetConstructor,Type.GetMethod,和Type.GetProperty通过提供某成员的签名信息来查找该成员。它们调用Binder.SelectMethod和Binder.SelectProperty以选择适合方法的签名信息。
Reflection提供诸如MicrosoftVisualBasic.NET和JScript语言编译器使用的底层结构来实施隐性后绑定。绑定是定位与某一特定类型相对应的声明的过程。当这个过程发生在运行的时候,而不是编译的时候,它被称为后绑定。VisualBasic.NET使你可以在你的代码中使用隐性后绑定;VisualBasic.NET编译器调用helper方法,使用Reflection获得对象类型。传递给helper方法的参数使适当的方法可以在运行时被调用。这些参数是调用方法(对象)的实例,被调用方法的名字(字符串),及传递给被调用方法的参数。(一个对象数组)。
在以下代码例子中,VisualBasic.NET编译器通过Reflection隐性地来对一在编译时不知类型的对象调用方法。HelloWorld类有一种PrintHello方法,可以打印出"HelloWorld"及传递给PrintHello方法的一些文本。本例中PrintHello方法调用实际上是Type.InvokeMember;VisualBasic代码允许PrintHello方法被调用,仿佛对象的类型(helloObj)在编译时就已经知道了(前期绑定),而不是在运行时(后绑定)。
[VisualBasic]
ImportsSystem
ModuleHello
SubMain()
'Setupvariable.
DimhelloObjAsObject
'Createtheobject.
helloObj=newHelloWorld()
'Invoketheprintmethodasifitwasearlybound
'eventhoughit'sreallylatebound.
helloObj.PrintHello("VisualBasicLateBound")
EndSub
EndModule
自定义绑定
Reflection除了可以隐性地被编译器用于后绑定,也可以在代码中显示使用,来完成后绑定。
commonlanguageruntime支持多种编程语言,这些语言的绑定规则互不相同。在前绑定的情况下,代码生成器能完全控制绑定。然而,在使用Reflection的后绑定中,绑定必须由自定义绑定控制。Binder类提供成员选择与调用的自定义控制。
使用自定义绑定,您可以在运行时装载assembly,获得assembly中关于类型的信息,指明您索要的类型,并且调用方法,访问字段,或类型的属性。如果在编译时您不知道对象的类型,该技术就显得格外有用,比如,当对象类型依赖于用户输入时。以下例子中的代码显示了在HelloWorld.dllassembly中,被动态使用Reflection调用的方法,第一个在VisualBasic.NET,第二个在C#中。
[VisualBasic]
'ThisclassisdeployedasanassemblyconsistingHelloWorldstring.
Privatem_helloWorldAsString="HelloWorld"
'Defaultpublicconstructor.
PublicSubNew()
EndSub'New
'Print"HelloWorld"plusthepassedtext.
PublicSubPrintHello(txtAsString)
'OutputtotheConsole.
Console.WriteLine((m_helloWorld&""&txt))
EndSub
EndClass
ImportsSystem
ImportsSystem.Reflection
ModuleVisualBasicLateHello
SubMain()
'Setupthevariables.
DimassemasSystem.Reflection.Assembly
DimobjasObject
DimhelloTypeasType
DimprintMethodasMethodInfo
'Loadtheassemblytouse.
assem=System.Reflection.Assembly.Load("HelloWorld")
'Getthetypetousefromtheassembly.
helloType=assem.GetType("HelloWorld")
'Getthemethodtousefromthetype.
printMethod=helloType.GetMethod("PrintHello")
'Createaninstanceofthetype.
obj=Activator.CreateInstance(helloType)
'Createanarraytoholdthearguments.
Dimargs(1)asObject
'Setthearguments.
args(0)="FromVisualBasicLateBound"
'Invokethemethod.
printMethod.Invoke(obj,args)
EndSub
EndModule
以下为C#版:
[C#]
//ThisclassisdeployedasanassemblyconsistingofoneDLL,
//calledHelloWorld.dll.
usingSystem;
publicclassHelloWorld{
//ConstantHelloWorldstring.
privateconstStringm_helloWorld="HelloWorld";
//Defaultpublicconstructor.
publicHelloWorld(){
}
//Print"HelloWorld"plusthepassedtext.
publicvoidPrintHello(Stringtxt){
//OutputtotheConsole.
Console.WriteLine(m_helloWorld+""+txt);
}
}
//Illustratesreflection'slatebindingfunctionality.
//CallsthePrintHellomethodonadynamicallyloaded
//andcreatedinstanceoftheHelloWorldclass.
usingSystem;
usingSystem.Reflection;
publicclassCSharpLateHello{
publicstaticvoidMain(){
//Loadtheassemblytouse.
Assemblyassem=Assembly.Load("HelloWorld");
//Getthetypetousefromtheassembly.
TypehelloType=assem.GetType("HelloWorld");
//Getthemethodtocallfromthetype.
MethodInfoprintMethod=helloType.GetMethod("PrintHello");
//CreateaninstanceoftheHelloWorldclass.
Objectobj=Activator.CreateInstance(helloType);
//Createtheargsarray.
Object[]args=newObject[1];
//Setthearguments.
args[0]="FromCSharpLateBound";
//InvokethePrintHellomethod.
printMethod.Invoke(obj,args);
}
}
InvokeMember与CreateInstance
可以使用Type.InvokeMember来调用某类型成员。各种类的CreateInstance方法,例如System.Activator和System.Reflection.Assembly,是InvokeMember的专用形式,用于生成某类型新的实例。Binder类在这些方法中,被用于重载解析和参数转换。
以下例子中的代码显示了三种可能的参数转换及成员选择的组合。在Case1中,不需要参数转换或成员选择。在Case2中,只需要成员选择。在Case3中,只需要参数转换。
[C#]
publicclassCustomBinderDriver
{
publicstaticvoidMain(string[]arguments)
{
Typet=typeof(CustomBinderDriver);
CustomBinderbinder=newCustomBinder();
BindingFlagsflags=BindingFlags.InvokeMethod|BindingFlags.Instance|
BindingFlags.Public|BindingFlags.Static;
//Case1.Neitherargumentcoercionnormemberselectionisneeded.
args=newObject[]{};
t.InvokeMember("PrintBob",flags,binder,null,args);
//Case2.Onlymemberselectionisneeded.
args=newObject[]{42};
t.InvokeMember("PrintValue",flags,binder,null,args);
//Case3.Onlyargumentcoercionisneeded.
args=newObject[]{"5.5"};
t.InvokeMember("PrintNumber",flags,binder,null,args);
}
publicstaticvoidPrintBob()
{
Console.WriteLine("PrintBob");
}
publicstaticvoidPrintValue(longvalue)
{
Console.WriteLine("PrintValue({0})",value);
}
publicstaticvoidPrintValue(Stringvalue)
{
Console.WriteLine("PrintValue\"{0}\")",value);
}
publicstaticvoidPrintNumber(doublevalue)
{
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标志,否则不会有成员返回。
如果只有一个成员与名字对应,就不需要回调,也就完成到这个方法的绑定。Case1中的代码例子表明了这一点:只有一个可用的PrintBob方法,所以,不需要回调。
如在可用集中,有多于一个成员。所有这些方法被传递给BindTo方法,再由它选择适当的方法,并且返回。在Case2中的代码例子中,有两种叫做PrintValue的方法。合适的方法取决于对BindToMethod调用。
ChangeType执行参数转换,它把实际参数转变为选定方法的参数类型。即使类型已经完美匹配,ChangeType也会针对每个参数被调用。
在Case3中的代码例子中,值为"5.5"的String类型的一个实际参数以正式参数Double类型被传递给方法。要想调用成功,字符串值"5.5"必须被转变为一个double值。ChangeType执行了这种转变。
ChangeType仅执行无损失转换,如下表所示:
SourceTypeTargetType
AnytypeItsbasetype
AnytypeInterfaceitimplements
CharUInt16,UInt32,Int32,UInt64,Int64,Single,Double
ByteChar,UInt16,Int16,UInt32,Int32,UInt64,Int64,Single,Double
SByteInt16,Int32,Int64,Single,Double
UInt16UInt32,Int32,UInt64,Int64,Single,Double
Int16Int32,Int64,Single,Double
UInt32UInt64,Int64,Single,Double
Int32Int64,Single,Double
UInt64Single,Double
Int64Single,Double
SingleDouble
Non-referencetypeReferencetype
Type类有Get方法,可使用Binder类型的参数的来解析对某成员的引用。Type.GetConstructor,Type.GetMethod,和Type.GetProperty通过提供某成员的签名信息来查找该成员。它们调用Binder.SelectMethod和Binder.SelectProperty以选择适合方法的签名信息。