动态装载和使用类型
作者:微軟
Reflection提供諸如Microsoft?Visual?Basic.NET和JScript語言編譯器使用的底層結(jié)構(gòu)來實(shí)施隱性后綁定。綁定是定位與某一特定類型相對(duì)應(yīng)的聲明的過程。當(dāng)這個(gè)過程發(fā)生在運(yùn)行的時(shí)候,而不是編譯的時(shí)候,它被稱為后綁定。Visual?Basic.NET使你可以在你的代碼中使用隱性后綁定;VisualBasic.NET編譯器調(diào)用helper?方法,使用Reflection獲得對(duì)象類型。傳遞給helper?方法的參數(shù)?使適當(dāng)?shù)姆椒梢栽谶\(yùn)行時(shí)被調(diào)用。這些參數(shù)是調(diào)用方法(對(duì)象)的實(shí)例,被調(diào)用方法的名字(字符串),及傳遞給被調(diào)用方法的參數(shù)。(一個(gè)對(duì)象數(shù)組)。
在以下代碼例子中,?Visual?Basic.NET編譯器通過Reflection隱性地?來對(duì)一在編譯時(shí)不知類型的對(duì)象調(diào)用方法。HelloWorld?類有一種?PrintHello?方法,可以打印出?"Hello?World"?及傳遞給PrintHello?方法的一些文本。本例中PrintHello?方法?調(diào)用實(shí)際上是Type.?InvokeMember?;?Visual?Basic?代碼?允許PrintHello?方法被調(diào)用,仿佛?對(duì)象的類型?(helloObj)在編譯時(shí)就已經(jīng)知道了(前期綁定),而不是在運(yùn)行時(shí)(后綁定)。
[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?支持多種編程語言,這些語言的綁定規(guī)則互不相同。在前綁定的情況下,代碼生成器能完全控制綁定。然而,在使用Reflection的后綁定中,綁定必須由自定義綁定控制。Binder類提供成員選擇與調(diào)用的自定義控制。?
使用自定義綁定,?您可以在運(yùn)行時(shí)裝載assembly,獲得assembly中關(guān)于類型的信息,指明您索要的類型,并且調(diào)用方法,訪問字段,或類型的屬性。如果在編譯時(shí)您不知道對(duì)象的類型,該技術(shù)就顯得格外有用,比如,當(dāng)對(duì)象類型依賴于用戶輸入時(shí)。以下例子中的代碼顯示了在HelloWorld.dll?assembly?中,被動(dòng)態(tài)使用Reflection調(diào)用的方法,第一個(gè)在Visual?Basic.NET,第二個(gè)在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來調(diào)用某類型成員。各種類的CreateInstance?方法,例如System.Activator?和?System.Reflection.Assembly,是InvokeMember的專用形式,用于生成某類型新的實(shí)例。Binder類在這些方法中,被用于重載解析和參數(shù)轉(zhuǎn)換。
以下例子中的代碼顯示了三種可能的參數(shù)轉(zhuǎn)換及成員選擇的組合。在Case1中,?不需要參數(shù)轉(zhuǎn)換或成員選擇。在Case?2中,只需要成員選擇。在Case3中,?只需要參數(shù)轉(zhuǎn)換。
[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);
}
}
?
當(dāng)存在多于一個(gè)的同名成員時(shí),就需要有重載解析。Binder.BindToMethod?和Binder.BindToField?方法可以用來綁定到一個(gè)成員。Binder.BindToMethod也可以通過get?和set?屬性訪問器提供屬性解析。
BindToMethod?返回可被調(diào)用的MethodBase.?如無可用的調(diào)用則返回null.?如果無法調(diào)用,BindToMethod?返回?MethodBase?為?調(diào)用或?null。MethodBase返回值無需是match參數(shù)之一,盡管事實(shí)往往如此。
調(diào)用者?也許會(huì)想得到ByRef?參數(shù)的返回。所以,如果BindTo方法改動(dòng)過參數(shù)數(shù)組,Binder?允許客戶使參數(shù)數(shù)組映射回它原來的表格。為了實(shí)現(xiàn)這點(diǎn),調(diào)用者必須確保參數(shù)順序不變。當(dāng)參數(shù)由名字傳遞,Binder重新整理參數(shù)組,以供調(diào)用者察看。
可用成員是指那些在類型或任何基本類型中定義的那些成員。如果指明BindingFlags.NonPublic,任何訪問級(jí)別的成員都會(huì)在返回中。如果BindingFlags.NonPublic?沒有被指明,binder必須執(zhí)行訪問規(guī)則。當(dāng)指明Public或?NonPublic?綁定標(biāo)志,?你必須也指明Instance?或Static?標(biāo)志,?否則不會(huì)有成員返回。
如果只有一個(gè)成員與名字對(duì)應(yīng),就不需要回調(diào),也就完成到這個(gè)方法的綁定。Case?1?中的代碼例子表明了這一點(diǎn):只有一個(gè)可用的PrintBob?方法,?所以,不需要回調(diào)。
如在可用集中,有多于一個(gè)成員。所有這些方法被傳遞給BindTo方法,?再由它選擇適當(dāng)?shù)姆椒?#xff0c;并且返回。在?Case?2?中的代碼例子中,有兩種叫做PrintValue的方法。合適的方法取決于對(duì)BindToMethod調(diào)用。
ChangeType?執(zhí)行參數(shù)轉(zhuǎn)換,?它把實(shí)際參數(shù)轉(zhuǎn)變?yōu)檫x定方法的參數(shù)類型。即使類型已經(jīng)完美匹配,ChangeType也會(huì)針對(duì)每個(gè)參數(shù)被調(diào)用。?
在?Case?3?中的代碼例子中,?值為"5.5"的String類型的一個(gè)實(shí)際參數(shù)以正式參數(shù)Double類型被傳遞給方法。要想調(diào)用成功,字符串值"5.5"必須被轉(zhuǎn)變?yōu)橐粋€(gè)double值。ChangeType?執(zhí)行了這種轉(zhuǎn)變。
ChangeType?僅執(zhí)行無損失轉(zhuǎn)換,?如下表所示:
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類型的參數(shù)的來解析對(duì)某成員的引用。Type.GetConstructor,Type.?GetMethod?,?和?Type.GetProperty?通過提供某成員的簽名信息來查找該成員。它們調(diào)用Binder.SelectMethod和Binder.SelectProperty?以選擇適合方法的簽名信息。
Reflection提供諸如Microsoft?Visual?Basic.NET和JScript語言編譯器使用的底層結(jié)構(gòu)來實(shí)施隱性后綁定。綁定是定位與某一特定類型相對(duì)應(yīng)的聲明的過程。當(dāng)這個(gè)過程發(fā)生在運(yùn)行的時(shí)候,而不是編譯的時(shí)候,它被稱為后綁定。Visual?Basic.NET使你可以在你的代碼中使用隱性后綁定;VisualBasic.NET編譯器調(diào)用helper?方法,使用Reflection獲得對(duì)象類型。傳遞給helper?方法的參數(shù)?使適當(dāng)?shù)姆椒梢栽谶\(yùn)行時(shí)被調(diào)用。這些參數(shù)是調(diào)用方法(對(duì)象)的實(shí)例,被調(diào)用方法的名字(字符串),及傳遞給被調(diào)用方法的參數(shù)。(一個(gè)對(duì)象數(shù)組)。
在以下代碼例子中,?Visual?Basic.NET編譯器通過Reflection隱性地?來對(duì)一在編譯時(shí)不知類型的對(duì)象調(diào)用方法。HelloWorld?類有一種?PrintHello?方法,可以打印出?"Hello?World"?及傳遞給PrintHello?方法的一些文本。本例中PrintHello?方法?調(diào)用實(shí)際上是Type.?InvokeMember?;?Visual?Basic?代碼?允許PrintHello?方法被調(diào)用,仿佛?對(duì)象的類型?(helloObj)在編譯時(shí)就已經(jīng)知道了(前期綁定),而不是在運(yùn)行時(shí)(后綁定)。
[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?支持多種編程語言,這些語言的綁定規(guī)則互不相同。在前綁定的情況下,代碼生成器能完全控制綁定。然而,在使用Reflection的后綁定中,綁定必須由自定義綁定控制。Binder類提供成員選擇與調(diào)用的自定義控制。?
使用自定義綁定,?您可以在運(yùn)行時(shí)裝載assembly,獲得assembly中關(guān)于類型的信息,指明您索要的類型,并且調(diào)用方法,訪問字段,或類型的屬性。如果在編譯時(shí)您不知道對(duì)象的類型,該技術(shù)就顯得格外有用,比如,當(dāng)對(duì)象類型依賴于用戶輸入時(shí)。以下例子中的代碼顯示了在HelloWorld.dll?assembly?中,被動(dòng)態(tài)使用Reflection調(diào)用的方法,第一個(gè)在Visual?Basic.NET,第二個(gè)在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來調(diào)用某類型成員。各種類的CreateInstance?方法,例如System.Activator?和?System.Reflection.Assembly,是InvokeMember的專用形式,用于生成某類型新的實(shí)例。Binder類在這些方法中,被用于重載解析和參數(shù)轉(zhuǎn)換。
以下例子中的代碼顯示了三種可能的參數(shù)轉(zhuǎn)換及成員選擇的組合。在Case1中,?不需要參數(shù)轉(zhuǎn)換或成員選擇。在Case?2中,只需要成員選擇。在Case3中,?只需要參數(shù)轉(zhuǎn)換。
[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);
}
}
?
當(dāng)存在多于一個(gè)的同名成員時(shí),就需要有重載解析。Binder.BindToMethod?和Binder.BindToField?方法可以用來綁定到一個(gè)成員。Binder.BindToMethod也可以通過get?和set?屬性訪問器提供屬性解析。
BindToMethod?返回可被調(diào)用的MethodBase.?如無可用的調(diào)用則返回null.?如果無法調(diào)用,BindToMethod?返回?MethodBase?為?調(diào)用或?null。MethodBase返回值無需是match參數(shù)之一,盡管事實(shí)往往如此。
調(diào)用者?也許會(huì)想得到ByRef?參數(shù)的返回。所以,如果BindTo方法改動(dòng)過參數(shù)數(shù)組,Binder?允許客戶使參數(shù)數(shù)組映射回它原來的表格。為了實(shí)現(xiàn)這點(diǎn),調(diào)用者必須確保參數(shù)順序不變。當(dāng)參數(shù)由名字傳遞,Binder重新整理參數(shù)組,以供調(diào)用者察看。
可用成員是指那些在類型或任何基本類型中定義的那些成員。如果指明BindingFlags.NonPublic,任何訪問級(jí)別的成員都會(huì)在返回中。如果BindingFlags.NonPublic?沒有被指明,binder必須執(zhí)行訪問規(guī)則。當(dāng)指明Public或?NonPublic?綁定標(biāo)志,?你必須也指明Instance?或Static?標(biāo)志,?否則不會(huì)有成員返回。
如果只有一個(gè)成員與名字對(duì)應(yīng),就不需要回調(diào),也就完成到這個(gè)方法的綁定。Case?1?中的代碼例子表明了這一點(diǎn):只有一個(gè)可用的PrintBob?方法,?所以,不需要回調(diào)。
如在可用集中,有多于一個(gè)成員。所有這些方法被傳遞給BindTo方法,?再由它選擇適當(dāng)?shù)姆椒?#xff0c;并且返回。在?Case?2?中的代碼例子中,有兩種叫做PrintValue的方法。合適的方法取決于對(duì)BindToMethod調(diào)用。
ChangeType?執(zhí)行參數(shù)轉(zhuǎn)換,?它把實(shí)際參數(shù)轉(zhuǎn)變?yōu)檫x定方法的參數(shù)類型。即使類型已經(jīng)完美匹配,ChangeType也會(huì)針對(duì)每個(gè)參數(shù)被調(diào)用。?
在?Case?3?中的代碼例子中,?值為"5.5"的String類型的一個(gè)實(shí)際參數(shù)以正式參數(shù)Double類型被傳遞給方法。要想調(diào)用成功,字符串值"5.5"必須被轉(zhuǎn)變?yōu)橐粋€(gè)double值。ChangeType?執(zhí)行了這種轉(zhuǎn)變。
ChangeType?僅執(zhí)行無損失轉(zhuǎn)換,?如下表所示:
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類型的參數(shù)的來解析對(duì)某成員的引用。Type.GetConstructor,Type.?GetMethod?,?和?Type.GetProperty?通過提供某成員的簽名信息來查找該成員。它們調(diào)用Binder.SelectMethod和Binder.SelectProperty?以選擇適合方法的簽名信息。
總結(jié)
- 上一篇: 详解.NET的RAD功能
- 下一篇: 如何调试你的C#程序