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