何时以及如何使用 Ldvirtftn 操作码?
下面的示例程序是我试图掌握 ldvirtftn 操作码的用法。
您会看到名称表明这是将虚拟函数指针加载到堆栈时使用的操作码。在示例代码中,我创建了一个具有 2 个静态方法 Ldftn
和 Ldvirtftn
的类型,这两个方法都返回 Base.Method() 的开放委托
第一个函数Ldftn
使用ldftn
操作码,并且工作异常,因为Base.Method
是虚拟的。第二种方法使用 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());
}
}
The following example program is my trying to grasp the usage of the ldvirtftn
opcode
You see the name suggests this is the opcode to use when loading a virtual function pointer on to the stack. In the example code, I'm creating a type with 2 static methods Ldftn
and Ldvirtftn
, both of these methods return an open delegate of Base.Method()
the first function Ldftn
uses the ldftn
opcode, and works unexpectedly, as Base.Method
is virtual. The second method uses Ldvirtftn
and apparently created an invalid program.
What am I doing wrong? What is the purpose of this opcode?
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());
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
以下是
ldftn
案例中发生的情况。您的方法创建一个委托,该委托具有:Base.Method()
作为方法(不是静态)。您将此委托创建为
Action
,它恰好有一个参数。当您在这一行中调用此委托时:CLR 使用新的
Child
实例作为“第一个参数”。由于您调用的方法不是静态的,因此第一个参数将成为 this 指针。因此,这个调用就相当于这会导致在调用时(而不是在 ldftn 时间)进行单独的虚拟方法调度,因此
Child.Method()
被调用。这就是为什么它打印“Child”而不是您可能期望的“Base”。在
ldvirtftn
情况下,您得到的程序无效,因为您忘记了ldvirtftn
需要堆栈上的对象引用,而ldftn
不需要't。您可以尝试进行以下更改来了解发生的情况:
将
Base
或Child
的实际实例传递给委托构造函数,这是非静态方法的惯例。您会发现它会拒绝创建委托,因为参数数量不再匹配(Action
需要一个参数,但Method()
没有) .通过将
Action
更改为简单的Action
或通过使Method()
接受范围。在这两种情况下,您可能很快就会发现它符合您的预期。特别是,您会发现使用ldftn
创建的委托将始终调用Base.Method()
,即使您使用Child
实例创建它也是如此>.Here is what happens in the
ldftn
case. Your method creates a delegate that has:Base.Method()
as the method (which is not static).You create this delegate as
Action<Base>
, which happens to have one parameter. When you call this delegate in this line:the CLR uses the new
Child
instance as the “first argument”. Since the method you are calling is not static, the first argument becomes thethis
pointer. Thus, this call becomes equivalent toand this causes a separate virtual-method dispatch at invoke time (not at ldftn time), so
Child.Method()
gets called. This is why it prints “Child” instead of the “Base” that you probably expected.In the
ldvirtftn
case, you are getting an invalid program because you forgot thatldvirtftn
requires an object reference on the stack whileldftn
doesn’t.You could try making the following changes to understand what’s going on:
Instead of
null
, pass an actual instance ofBase
orChild
to the delegate constructor, as is customary for non-static methods. You will find that it will then refuse to create the delegate because the number of parameters no longer match (Action<Base>
requires one parameter, butMethod()
has none).Make the number of parameters match, either by changing
Action<Base>
to simplyAction
, or by makingMethod()
accept a parameter. In both cases, you will probably quickly find that it does what you expect. In particular, you will find that the delegate created withldftn
will always callBase.Method()
even if you created it with an instance ofChild
.