之前在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才只有幾天的時間,所以上面的分析都顯得有點膚淺,只是 簡單的記錄下自己的學習過程,如果各位看官能夠給我一點深層次的分析,我將 不甚感激。
本文配套源碼