下列程式示範如何定義和執行簡單的動態方法,以及系結至類別實例的動態方法。 如需動態方法的詳細資訊,請參閱 類別 DynamicMethod 。
宣告委派類型以執行 方法。 請考慮使用泛型委派,將您需要宣告的委派類型數目降到最低。 下列程式代碼會宣告兩個可用於 方法的
SquareIt委派型別,其中一個是泛型型別。private delegate long SquareItInvoker(int input); private delegate TReturn OneParameter<TReturn, TParameter0> (TParameter0 p0);Private Delegate Function _ SquareItInvoker(ByVal input As Integer) As Long Private Delegate Function _ OneParameter(Of TReturn, TParameter0) _ (ByVal p0 As TParameter0) As TReturn建立陣列,指定動態方法的參數類型。 在此範例中,唯一的參數是
int(在 Visual Basic 中是Integer),因此陣列只有一個元素。Type[] methodArgs = {typeof(int)};Dim methodArgs As Type() = {GetType(Integer)}建立 DynamicMethod。 在這裡範例中,方法名為
SquareIt。備註
不需要提供動態方法名稱,而且無法依名稱叫用它們。 多個動態方法可以有相同的名稱。 不過,名稱會出現在呼叫堆疊中,而且可用於偵錯。
傳回值的型別會指定為
long。 方法與包含 類別的Example模組相關聯,其中包含範例程序代碼。 可以指定任何載入的模組。 動態方法的作用就像模組層級static方法(Shared在 Visual Basic 中)。DynamicMethod squareIt = new DynamicMethod( "SquareIt", typeof(long), methodArgs, typeof(Example).Module);Dim squareIt As New DynamicMethod( _ "SquareIt", _ GetType(Long), _ methodArgs, _ GetType(Example).Module)產生方法主體。 在此範例中, ILGenerator 對象可用來發出通用中繼語言 (CIL)。 或者,DynamicILInfo 物件可以與非受控程式碼生成器搭配使用,以發出 DynamicMethod 的方法主體。
此範例中的 CIL 會將引數載入到堆疊,將其轉換成
int、複製long,並將這兩個數字相乘。 這會將平方結果保留在堆疊上,然後方法只需返回即可。ILGenerator il = squareIt.GetILGenerator(); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Conv_I8); il.Emit(OpCodes.Dup); il.Emit(OpCodes.Mul); il.Emit(OpCodes.Ret);Dim il As ILGenerator = squareIt.GetILGenerator() il.Emit(OpCodes.Ldarg_0) il.Emit(OpCodes.Conv_I8) il.Emit(OpCodes.Dup) il.Emit(OpCodes.Mul) il.Emit(OpCodes.Ret)藉由呼叫 CreateDelegate 方法,建立委派的實例(在步驟 1 中宣告), 代表動態方法。 建立委派後,該方法被視為已完成,並且任何嘗試進一步修改方法,例如新增更多 CIL,都將被忽略。 下列程式代碼會建立委派,並使用泛型委派叫用它。
OneParameter<long, int> invokeSquareIt = (OneParameter<long, int>) squareIt.CreateDelegate(typeof(OneParameter<long, int>)); Console.WriteLine($"123456789 squared = {invokeSquareIt(123456789)}");Dim invokeSquareIt As OneParameter(Of Long, Integer) = _ CType( _ squareIt.CreateDelegate( _ GetType(OneParameter(Of Long, Integer))), _ OneParameter(Of Long, Integer) _ ) Console.WriteLine("123456789 squared = {0}", _ invokeSquareIt(123456789))宣告委派類型以執行 方法。 請考慮使用泛型委派,將您需要宣告的委派類型數目降到最低。 下列程式代碼會宣告泛型委派類型,這個型別可用來使用一個參數和傳回值來執行任何方法,或者,如果委派系結至物件,則為具有兩個參數和傳回值的方法。
private delegate TReturn OneParameter<TReturn, TParameter0> (TParameter0 p0);Private Delegate Function _ OneParameter(Of TReturn, TParameter0) _ (ByVal p0 As TParameter0) As TReturn建立陣列,指定動態方法的參數類型。 如果表示方法的委派要系結至物件,則第一個參數必須符合委派所系結的類型。 在此範例中,有兩個參數,類型分別為
Example和int(在 Visual Basic 中,類型為Integer)。Type[] methodArgs2 = { typeof(Example), typeof(int) };Dim methodArgs2 As Type() = _ {GetType(Example), GetType(Integer)}建立 DynamicMethod。 在此範例中,方法沒有名稱。 傳回值的型別指定為
int(Integer在 Visual Basic 中)。 方法可以存取Example類別的私用和受保護成員。DynamicMethod multiplyHidden = new DynamicMethod( "", typeof(int), methodArgs2, typeof(Example));Dim multiplyPrivate As New DynamicMethod( _ "", _ GetType(Integer), _ methodArgs2, _ GetType(Example))產生方法主體。 在此範例中, ILGenerator 對象可用來發出通用中繼語言 (CIL)。 或者,DynamicILInfo 物件可以與非受控程式碼生成器搭配使用,以發出 DynamicMethod 的方法主體。
此範例中的 CIL 會載入第一個自變數,這是 類別的
Example實例,並用它來載入 類型int私用實例欄位的值。 載入第二個自變數,並將兩個數位相乘。 如果結果大於int,則會截斷值,並捨棄最重要的位。 方法會傳回,並將傳回值置於堆疊上。ILGenerator ilMH = multiplyHidden.GetILGenerator(); ilMH.Emit(OpCodes.Ldarg_0); FieldInfo testInfo = typeof(Example).GetField("test", BindingFlags.NonPublic | BindingFlags.Instance); ilMH.Emit(OpCodes.Ldfld, testInfo); ilMH.Emit(OpCodes.Ldarg_1); ilMH.Emit(OpCodes.Mul); ilMH.Emit(OpCodes.Ret);Dim ilMP As ILGenerator = multiplyPrivate.GetILGenerator() ilMP.Emit(OpCodes.Ldarg_0) Dim testInfo As FieldInfo = _ GetType(Example).GetField("test", _ BindingFlags.NonPublic Or BindingFlags.Instance) ilMP.Emit(OpCodes.Ldfld, testInfo) ilMP.Emit(OpCodes.Ldarg_1) ilMP.Emit(OpCodes.Mul) ilMP.Emit(OpCodes.Ret)藉由呼叫 CreateDelegate(Type, Object) 方法多載,建立委派的實例(在步驟 1 中宣告),代表動態方法。 創建委派將完成該方法,並且任何進一步嘗試更改該方法的行為,例如添加更多 CIL 指令,將被忽略。
備註
您可以多次呼叫 CreateDelegate 方法,以建立系結至目標類型其他實例的委派。
下列程式代碼會將 方法系結至類別的新實例
Example,其私用測試字段設定為 42。 也就是說,每次叫用委派時,實例Example都會傳遞至 方法的第一個參數。由於方法的第一個參數始終接收
OneParameter的實例,因此使用了委派Example。 委派被叫用時,只需要提供第二個參數。OneParameter<int, int> invoke = (OneParameter<int, int>) multiplyHidden.CreateDelegate( typeof(OneParameter<int, int>), new Example(42) ); Console.WriteLine($"3 * test = {invoke(3)}");Dim invoke As OneParameter(Of Integer, Integer) = _ CType( _ multiplyPrivate.CreateDelegate( _ GetType(OneParameter(Of Integer, Integer)), _ new Example(42) _ ), _ OneParameter(Of Integer, Integer) _ ) Console.WriteLine("3 * test = {0}", invoke(3))
範例
下列程式代碼範例示範簡單的動態方法和系結至 類別實例的動態方法。
簡單的動態方法會採用一個自變數、32 位整數,並傳回該整數的64位平方。 泛型委派可用來叫用 方法。
第二個動態方法有兩個參數,類型為 Example 和類型為 int(在 Visual Basic 中為Integer)。 建立動態方法時,會使用具有一個類型Example自變數的泛型委派,系結到int的實例。 委派沒有類型為 Example 的參數,因為方法的第一個參數總是接收 Example 的系結實例。 當委派被調用時,只會提供int參數。 這個動態方法會存取 類別的私用 Example 字段,並傳回私用字段和 int 自變數的乘積。
程式代碼範例會定義可用來執行方法的委派。
using System;
using System.Reflection;
using System.Reflection.Emit;
public class Example
{
// The following constructor and private field are used to
// demonstrate a method bound to an object.
private int test;
public Example(int test) { this.test = test; }
// Declare delegates that can be used to execute the completed
// SquareIt dynamic method. The OneParameter delegate can be
// used to execute any method with one parameter and a return
// value, or a method with two parameters and a return value
// if the delegate is bound to an object.
//
private delegate long SquareItInvoker(int input);
private delegate TReturn OneParameter<TReturn, TParameter0>
(TParameter0 p0);
public static void Main()
{
// Example 1: A simple dynamic method.
//
// Create an array that specifies the parameter types for the
// dynamic method. In this example the only parameter is an
// int, so the array has only one element.
//
Type[] methodArgs = {typeof(int)};
// Create a DynamicMethod. In this example the method is
// named SquareIt. It is not necessary to give dynamic
// methods names. They cannot be invoked by name, and two
// dynamic methods can have the same name. However, the
// name appears in calls stacks and can be useful for
// debugging.
//
// In this example the return type of the dynamic method
// is long. The method is associated with the module that
// contains the Example class. Any loaded module could be
// specified. The dynamic method is like a module-level
// static method.
//
DynamicMethod squareIt = new DynamicMethod(
"SquareIt",
typeof(long),
methodArgs,
typeof(Example).Module);
// Emit the method body. In this example ILGenerator is used
// to emit the MSIL. DynamicMethod has an associated type
// DynamicILInfo that can be used in conjunction with
// unmanaged code generators.
//
// The MSIL loads the argument, which is an int, onto the
// stack, converts the int to a long, duplicates the top
// item on the stack, and multiplies the top two items on the
// stack. This leaves the squared number on the stack, and
// all the method has to do is return.
//
ILGenerator il = squareIt.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Conv_I8);
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Mul);
il.Emit(OpCodes.Ret);
// Create a delegate that represents the dynamic method.
// Creating the delegate completes the method, and any further
// attempts to change the method (for example, by adding more
// MSIL) are ignored. The following code uses a generic
// delegate that can produce delegate types matching any
// single-parameter method that has a return type.
//
OneParameter<long, int> invokeSquareIt =
(OneParameter<long, int>)
squareIt.CreateDelegate(typeof(OneParameter<long, int>));
Console.WriteLine($"123456789 squared = {invokeSquareIt(123456789)}");
// Example 2: A dynamic method bound to an instance.
//
// Create an array that specifies the parameter types for a
// dynamic method. If the delegate representing the method
// is to be bound to an object, the first parameter must
// match the type the delegate is bound to. In the following
// code the bound instance is of the Example class.
//
Type[] methodArgs2 = { typeof(Example), typeof(int) };
// Create a DynamicMethod. In this example the method has no
// name. The return type of the method is int. The method
// has access to the protected and private data of the
// Example class.
//
DynamicMethod multiplyHidden = new DynamicMethod(
"",
typeof(int),
methodArgs2,
typeof(Example));
// Emit the method body. In this example ILGenerator is used
// to emit the MSIL. DynamicMethod has an associated type
// DynamicILInfo that can be used in conjunction with
// unmanaged code generators.
//
// The MSIL loads the first argument, which is an instance of
// the Example class, and uses it to load the value of a
// private instance field of type int. The second argument is
// loaded, and the two numbers are multiplied. If the result
// is larger than int, the value is truncated and the most
// significant bits are discarded. The method returns, with
// the return value on the stack.
//
ILGenerator ilMH = multiplyHidden.GetILGenerator();
ilMH.Emit(OpCodes.Ldarg_0);
FieldInfo testInfo = typeof(Example).GetField("test",
BindingFlags.NonPublic | BindingFlags.Instance);
ilMH.Emit(OpCodes.Ldfld, testInfo);
ilMH.Emit(OpCodes.Ldarg_1);
ilMH.Emit(OpCodes.Mul);
ilMH.Emit(OpCodes.Ret);
// Create a delegate that represents the dynamic method.
// Creating the delegate completes the method, and any further
// attempts to change the method — for example, by adding more
// MSIL — are ignored.
//
// The following code binds the method to a new instance
// of the Example class whose private test field is set to 42.
// That is, each time the delegate is invoked the instance of
// Example is passed to the first parameter of the method.
//
// The delegate OneParameter is used, because the first
// parameter of the method receives the instance of Example.
// When the delegate is invoked, only the second parameter is
// required.
//
OneParameter<int, int> invoke = (OneParameter<int, int>)
multiplyHidden.CreateDelegate(
typeof(OneParameter<int, int>),
new Example(42)
);
Console.WriteLine($"3 * test = {invoke(3)}");
}
}
/* This code example produces the following output:
123456789 squared = 15241578750190521
3 * test = 126
*/
Imports System.Reflection
Imports System.Reflection.Emit
Public Class Example
' The following constructor and private field are used to
' demonstrate a method bound to an object.
'
Private test As Integer
Public Sub New(ByVal test As Integer)
Me.test = test
End Sub
' Declare delegates that can be used to execute the completed
' SquareIt dynamic method. The OneParameter delegate can be
' used to execute any method with one parameter and a return
' value, or a method with two parameters and a return value
' if the delegate is bound to an object.
'
Private Delegate Function _
SquareItInvoker(ByVal input As Integer) As Long
Private Delegate Function _
OneParameter(Of TReturn, TParameter0) _
(ByVal p0 As TParameter0) As TReturn
Public Shared Sub Main()
' Example 1: A simple dynamic method.
'
' Create an array that specifies the parameter types for the
' dynamic method. In this example the only parameter is an
' Integer, so the array has only one element.
'
Dim methodArgs As Type() = {GetType(Integer)}
' Create a DynamicMethod. In this example the method is
' named SquareIt. It is not necessary to give dynamic
' methods names. They cannot be invoked by name, and two
' dynamic methods can have the same name. However, the
' name appears in calls stacks and can be useful for
' debugging.
'
' In this example the return type of the dynamic method
' is Long. The method is associated with the module that
' contains the Example class. Any loaded module could be
' specified. The dynamic method is like a module-level
' Shared method.
'
Dim squareIt As New DynamicMethod( _
"SquareIt", _
GetType(Long), _
methodArgs, _
GetType(Example).Module)
' Emit the method body. In this example ILGenerator is used
' to emit the MSIL. DynamicMethod has an associated type
' DynamicILInfo that can be used in conjunction with
' unmanaged code generators.
'
' The MSIL loads the argument, which is an Integer, onto the
' stack, converts the Integer to a Long, duplicates the top
' item on the stack, and multiplies the top two items on the
' stack. This leaves the squared number on the stack, and
' all the method has to do is return.
'
Dim il As ILGenerator = squareIt.GetILGenerator()
il.Emit(OpCodes.Ldarg_0)
il.Emit(OpCodes.Conv_I8)
il.Emit(OpCodes.Dup)
il.Emit(OpCodes.Mul)
il.Emit(OpCodes.Ret)
' Create a delegate that represents the dynamic method.
' Creating the delegate completes the method, and any further
' attempts to change the method (for example, by adding more
' MSIL) are ignored. The following code uses a generic
' delegate that can produce delegate types matching any
' single-parameter method that has a return type.
'
Dim invokeSquareIt As OneParameter(Of Long, Integer) = _
CType( _
squareIt.CreateDelegate( _
GetType(OneParameter(Of Long, Integer))), _
OneParameter(Of Long, Integer) _
)
Console.WriteLine("123456789 squared = {0}", _
invokeSquareIt(123456789))
' Example 2: A dynamic method bound to an instance.
'
' Create an array that specifies the parameter types for a
' dynamic method. If the delegate representing the method
' is to be bound to an object, the first parameter must
' match the type the delegate is bound to. In the following
' code the bound instance is of the Example class.
'
Dim methodArgs2 As Type() = _
{GetType(Example), GetType(Integer)}
' Create a DynamicMethod. In this example the method has no
' name. The return type of the method is Integer. The method
' has access to the protected and private members of the
' Example class.
'
Dim multiplyPrivate As New DynamicMethod( _
"", _
GetType(Integer), _
methodArgs2, _
GetType(Example))
' Emit the method body. In this example ILGenerator is used
' to emit the MSIL. DynamicMethod has an associated type
' DynamicILInfo that can be used in conjunction with
' unmanaged code generators.
'
' The MSIL loads the first argument, which is an instance of
' the Example class, and uses it to load the value of a
' private instance field of type Integer. The second argument
' is loaded, and the two numbers are multiplied. If the result
' is larger than Integer, the value is truncated and the most
' significant bits are discarded. The method returns, with
' the return value on the stack.
'
Dim ilMP As ILGenerator = multiplyPrivate.GetILGenerator()
ilMP.Emit(OpCodes.Ldarg_0)
Dim testInfo As FieldInfo = _
GetType(Example).GetField("test", _
BindingFlags.NonPublic Or BindingFlags.Instance)
ilMP.Emit(OpCodes.Ldfld, testInfo)
ilMP.Emit(OpCodes.Ldarg_1)
ilMP.Emit(OpCodes.Mul)
ilMP.Emit(OpCodes.Ret)
' Create a delegate that represents the dynamic method.
' Creating the delegate completes the method, and any further
' attempts to change the method for example, by adding more
' MSIL are ignored.
'
' The following code binds the method to a new instance
' of the Example class whose private test field is set to 42.
' That is, each time the delegate is invoked the instance of
' Example is passed to the first parameter of the method.
'
' The delegate OneParameter is used, because the first
' parameter of the method receives the instance of Example.
' When the delegate is invoked, only the second parameter is
' required.
'
Dim invoke As OneParameter(Of Integer, Integer) = _
CType( _
multiplyPrivate.CreateDelegate( _
GetType(OneParameter(Of Integer, Integer)), _
new Example(42) _
), _
OneParameter(Of Integer, Integer) _
)
Console.WriteLine("3 * test = {0}", invoke(3))
End Sub
End Class
' This code example produces the following output:
'
'123456789 squared = 15241578750190521
'3 * test = 126
'