动态装载和使用类型
作者:微軟
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?以選擇適合方法的簽名信息。
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?以選擇適合方法的簽名信息。
總結
- 上一篇: 详解.NET的RAD功能
- 下一篇: 如何调试你的C#程序