之前在Emit的學習過程中,多次碰到了方法的調用,發現有時候是使用Call而 有時候是使用Callvirt,一直對這兩者的區別不甚了解。然後就查閱了MSDN, MSDN中對這兩者的解釋為:
l Call:調用由傳遞的方法說明符指示的方法;
l Callvirt:對對象調用後期綁定方法,並且將返回值推送到計算堆棧上。
但是看了之後還是很不明白,我想可能是因為中文版的緣故吧。今天下午再次 看到了對Callvirt指令的解釋,“對對象調用後期綁定方法”,突然想到,這個 好像是指多態的意思吧?在一看virt,應該就是virtual的縮寫,於是就更加肯定 了自己的想法(外派在農行,不能上網,不然在園子隨便一找就有結果了,傷心 啊!),立馬動手開始實踐。
我們用最經典的Animal的例子來驗證這個想法,首先定義相關的類型,如下:
Animal
class Animal
{
public virtual void Speak()
{
Console.WriteLine("Animal.Speak");
}
}
class Cat : Animal
{
public override void Speak()
{
Console.WriteLine("Cat.Speak");
}
}
class Dog : Animal
{
public override void Speak()
{
Console.WriteLine("Dog.Speak");
}
}
由於只是實現簡單的方法調用,所以我們在這裡選擇使用DynamicMethod而不 再創建動態程序集,順便也可以演練下DynamicMethod的使用。要使用 DynamicMethod我們首先要定義一個委托,用來執行方法的調用,定義如下:
private delegate void SpeakDelegate(Animal animal);
到時候我們通過此委托,傳入一個Animal類或者其派生類的實例,並調用裡面 的Speak方法,從而驗證之前的想法。由於方法的實現比較簡單,這裡就直接通過 代碼的注釋進行講解,代碼如下:
DynamicMethod
class Program
{
private delegate void SpeakDelegate(Animal animal);
static void Main(string[] args)
{
//定義動態方法,沒有返回值,傳入參數為Animal,所在的模塊
選擇為Program類所在的模塊
DynamicMethod dynamicSpeakWithCall = new DynamicMethod
("DynamicSpeakWithCall", null, new Type[] { typeof(Animal) }, typeof
(Program).Module);
ILGenerator callIL =
dynamicSpeakWithCall.GetILGenerator();
//加載參數0 即 Animal類或其派生類的對象
callIL.Emit(OpCodes.Ldarg_0);
//通過Call指令調用Speak方法
callIL.Emit(OpCodes.Call, typeof(Animal).GetMethod
("Speak"));
callIL.Emit(OpCodes.Ret);
Console.WriteLine("SpeakWithCall:");
SpeakDelegate SpeakWithCall = (SpeakDelegate)
dynamicSpeakWithCall.CreateDelegate(typeof(SpeakDelegate));
SpeakWithCall(new Animal());
SpeakWithCall(new Cat());
SpeakWithCall(new Dog());
//定義動態方法,沒有返回值,傳入參數為Animal,所在的模塊
選擇為Program類所在的模塊
DynamicMethod dynamicSpeakWithCallvirt = new
DynamicMethod("DynamicSpeakWithCallvirt", null, new Type[] { typeof
(Animal) }, typeof(Program).Module);
ILGenerator callvirtIL =
dynamicSpeakWithCallvirt.GetILGenerator();
//加載參數0 即 Animal類或其派生類的對象
callvirtIL.Emit(OpCodes.Ldarg_0);
//通過Callvirt指令調用Speak方法
callvirtIL.Emit(OpCodes.Callvirt, typeof
(Animal).GetMethod("Speak"));
callvirtIL.Emit(OpCodes.Ret);
Console.WriteLine("SpeakWithCallvirt:");
SpeakDelegate SpeakWithCallvirt = (SpeakDelegate)
dynamicSpeakWithCallvirt.CreateDelegate(typeof(SpeakDelegate));
SpeakWithCallvirt(new Animal());
SpeakWithCallvirt(new Cat());
SpeakWithCallvirt(new Dog());
}
}
最後給出相應的輸出結果:
SpeakWithCall: Animal.Speak Animal.Speak Animal.Speak SpeakWithCallvirt: Animal.Speak Cat.Speak Dog.Speak
PS:由於學習Emit才只有幾天的時間,所以上面的分析都顯得有點膚淺,只是 簡單的記錄下自己的學習過程,如果各位看官能夠給我一點深層次的分析,我將 不甚感激。
本文配套源碼