2010-12-16 26 views
2

次のプログラム例は、オペコードldvirtftnの使用方法を把握しようとしています。名前には、仮想関数ポインタをスタックにロードするときに使用するオペコードであることが示されています。例のコードでは、私は2つの静的メソッドLdftnLdvirtftnの型を作成していますが、これらのメソッドは両方ともBase.Method()のオープンデリゲートを返します。Ldftnldftnオペコードを使用し、Base.Methodは仮想です。 2番目の方法はLdvirtftnを使用し、無効なプログラムを作成したようです。私は間違って何をしていますか?混乱以外のこのオペコードの目的は何ですか?ここでLdvirtftnオペコードはいつ、どのように使用しますか?

public class Base 
{ 
    public virtual void Method() 
    { 
     Console.WriteLine("Base"); 
    } 
} 

public class Child : Base 
{ 
    public override void Method() 
    { 
     Console.WriteLine("Child"); 
    } 
} 
class Program 
{ 
    static void Main(string[] args) 
    { 
     AssemblyBuilder ab =AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("Test"),AssemblyBuilderAccess.RunAndSave); 
     ModuleBuilder mb = ab.DefineDynamicModule("TestModule"); 
     TypeBuilder tb = mb.DefineType("TestType"); 
     MethodBuilder method = tb.DefineMethod("Ldftn",MethodAttributes.Public | MethodAttributes.Static, typeof(Action<Base>), Type.EmptyTypes); 
     var ilgen = method.GetILGenerator(); 
     ilgen.Emit(OpCodes.Ldnull); 
     ilgen.Emit(OpCodes.Ldftn, typeof(Base).GetMethod("Method")); 
     ilgen.Emit(OpCodes.Newobj, typeof(Action<Base>).GetConstructors()[0]); 
     ilgen.Emit(OpCodes.Ret); 
     method = tb.DefineMethod("Ldvirtftn", MethodAttributes.Public | MethodAttributes.Static, typeof(Action<Base>), Type.EmptyTypes); 
     ilgen = method.GetILGenerator(); 
     ilgen.Emit(OpCodes.Ldnull); 
     ilgen.Emit(OpCodes.Ldvirtftn, typeof(Base).GetMethod("Method")); 
     ilgen.Emit(OpCodes.Newobj, typeof(Action<Base>).GetConstructors()[0]); 
     ilgen.Emit(OpCodes.Ret); 
     var type = tb.CreateType(); 
     var func = Delegate.CreateDelegate(typeof(Func<Action<Base>>),tb.GetMethod("Ldftn")) as Func<Action<Base>>; 
     var func2 = Delegate.CreateDelegate(typeof(Func<Action<Base>>), tb.GetMethod("Ldvirtftn")) as Func<Action<Base>>; 
     func()(new Child()); 
     func2()(new Child()); 
    } 
} 

答えて

7
  1. ldftn場合に何が起こるかです。あなたのメソッドは、以下を持つデリゲートを作成します。

    • (通常は静的メソッドにのみ使用されます);
    • Base.Method()(方法はではなく、静的)です。

    このデリゲートは、Action<Base>という名前で作成されますが、これには1つのパラメータが使用されます。あなたは、この行では、このデリゲートを呼び出すとき:

    func()(new Child()); 
    

    CLRは、「第一引数」として新しいChildインスタンスを使用しています。呼び出すメソッドがでないであるため、最初の引数はthisポインタになります。したがって、この呼び出しは

    new Child().Method(); 
    

    と等価になると、これは呼出し時間(ldftnない時)に別の仮想法ディスパッチを引き起こすので、Child.Method()が呼び出されます。これは、おそらく "Base"の代わりに "Child"を印刷する理由です。 ldvirtftn場合

  2. あなたはldftnはそうではないldvirtftnは、スタック上のオブジェクト参照が必要であることを忘れてしまったので、無効なプログラムを取得しています。

あなたは何が起こっているか理解するために、以下の変更を行ってみてください:

  • 代わりのnull、非のための通例であるように、デリゲートコンストラクタにBaseまたはChildの実際のインスタンスを渡します静的メソッド。パラメータの数が一致しなくなるため、デリゲートの作成が拒否されます(Action<Base>には1つのパラメータが必要ですが、Method()には存在しません)。

  • Action<Base>を単にActionに変更するか、またはMethod()にパラメータを受け入れることによって、パラメータの数を一致させます。両方のケースで、おそらくあなたが期待していることをすばやく見つけることができます。特に、ldftnで作成されたデリゲートは、Childのインスタンスで作成した場合でも常にBase.Method()を呼び出します。

+0

実際、仮想ディスパッチは、私が後にしたものでした。私はvirtftnがインスタンスを渡している間だけ動作していることを知らなかったので、行くために送ると思います:ld instance、dup、ldvirtftnメソッド、newobj delegate ctor? –

+0

"static" 'Func 'デリゲートと ''インスタンス "' Func 'デリゲートの両方を作成するのに、静的な' string - > int'関数を使うことができます。いずれかのデリゲート型の 'ldnull'(または' ldstr')+ 'ldftn' +' newobj'です。 –

関連する問題