2010-12-16 1 views
2

Следующая примерная программа - это попытка использовать код операции ldvirtftn. Вы видите, что это имя указывает на то, что код операции используется при загрузке указателя виртуальной функции в стек. В примере кода я создаю тип с 2 статическими методами Ldftn и Ldvirtftn, оба этих метода возвращают открытый делегат Base.Method(), первая функция Ldftn использует код операции ldftn и работает неожиданно, так как Base.Method является виртуальным. Второй метод использует 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>, который имеет один параметр. При вызове этого делегата в этой строке:

    func()(new Child()); 
    

    СЬК использует новый Child экземпляр в качестве «первого аргумента». Поскольку метод, который вы вызываете, это не статический, первый аргумент становится указателем this. Таким образом, этот вызов становится эквивалентным

    new Child().Method(); 
    

    и это приводит к отдельной виртуальной метод отправки во время Invoke (не во время ldftn), так Child.Method() вызывается. Вот почему он печатает «Ребенок» вместо «Базы», ​​который вы, вероятно, ожидали.

  2. В ldvirtftn случае вы получаете неверную программу, потому что вы забыли, что ldvirtftn требует ссылки на объект в стеке, а ldftn нет.

Вы можете попробовать сделать следующие изменения, чтобы понять, что происходит:

  • Вместо null, передать реальный экземпляр Base или Child конструктору делегата, как это принято для не- статические методы. Вы обнаружите, что тогда он откажется от создания делегата, потому что количество параметров больше не соответствует (Action<Base> требует один параметр, но Method() не имеет).

  • Сопоставьте количество параметров, либо изменив Action<Base> на просто Action, либо путем принятия Method() принять параметр. В обоих случаях вы, вероятно, быстро обнаружите, что он делает то, что вы ожидаете. В частности, вы обнаружите, что делегат, созданный с помощью ldftn, всегда будет звонить Base.Method(), даже если вы создали его с экземпляром Child.

+0

Фактически виртуальная отправка была тем, чем я был. Я не знал, что virtftn работает только при передаче экземпляра, поэтому я должен отправить его: ld instance, dup, ldvirtftn Method, newobj delegate ctor? – 2010-12-16 13:50:33

Смежные вопросы