动态装载和使用类型

作者:微软

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以选择适合方法的签名信息。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值