C# 何时以及如何使用LDVIRTTN操作码?
下面的示例程序是我试图掌握C# 何时以及如何使用LDVIRTTN操作码?,c#,.net,reflection.emit,il,C#,.net,Reflection.emit,Il,下面的示例程序是我试图掌握ldvirtftnopcode的用法的程序。您可以看到,名称表明这是在堆栈上加载虚拟函数指针时要使用的操作码。在示例代码中,我使用两个静态方法创建了一个类型Ldftn和Ldvirtftn,这两个方法都返回一个打开的委托Base.Method()第一个函数Ldftn使用Ldftn操作码,并意外工作,因为Base.Method是虚拟的。第二种方法使用Ldvirtftn,显然创建了一个无效的程序。我做错了什么?除了混淆之外,此操作码的目的是什么 public class Ba
ldvirtftn
opcode的用法的程序。您可以看到,名称表明这是在堆栈上加载虚拟函数指针时要使用的操作码。在示例代码中,我使用两个静态方法创建了一个类型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());
}
}
公共类基
{
公共虚空方法()
{
控制台。写入线(“基”);
}
}
公共类子类:基
{
公共重写无效方法()
{
控制台。书写线(“儿童”);
}
}
班级计划
{
静态void Main(字符串[]参数)
{
AssemblyBuilder ab=AppDomain.CurrentDomain.DefinedDynamicAssembly(新的AssemblyName(“测试”),AssemblyBuilderAccess.RunAndSave);
ModuleBuilder mb=ab.DefinedDynamicModule(“测试模块”);
TypeBuilder tb=mb.DefineType(“TestType”);
MethodBuilder method=tb.DefineMethod(“Ldftn”,MethodAttributes.Public | MethodAttributes.Static,typeof(Action),Type.EmptyTypes);
var ilgen=method.GetILGenerator();
ilgen.Emit(操作码.Ldnull);
Emit(opcode.Ldftn,typeof(Base.GetMethod)(“Method”);
Emit(opcode.Newobj,typeof(Action.GetConstructors()[0]);
ilgen.Emit(操作码Ret);
method=tb.DefineMethod(“Ldvirtftn”,MethodAttributes.Public | MethodAttributes.Static,typeof(Action),Type.EmptyTypes);
ilgen=method.GetILGenerator();
ilgen.Emit(操作码.Ldnull);
Emit(opcode.Ldvirtftn,typeof(Base.GetMethod)(“方法”);
Emit(opcode.Newobj,typeof(Action.GetConstructors()[0]);
ilgen.Emit(操作码Ret);
var type=tb.CreateType();
var func=Delegate.CreateDelegate(typeof(func),tb.GetMethod(“Ldftn”))作为func;
var func2=Delegate.CreateDelegate(typeof(Func),tb.GetMethod(“Ldvirtftn”))作为Func;
func()(新子项());
func2()(新子项());
}
}
ldftn
案例中发生的情况。您的方法创建的委托具有:
- 没有第一个参数(通常只用于静态方法)李>
作为方法(不是静态的)Base.Method()
操作
,它恰好有一个参数。在此行中调用此代理时:
func()(new Child());
CLR使用新的子实例作为“第一个参数”。由于您调用的方法不是静态的,因此第一个参数成为this
指针。因此,此调用相当于
new Child().Method();
这会导致在调用时(而不是在ldftn时)单独分派虚拟方法,因此会调用Child.method()
。这就是为什么它打印“Child”而不是您可能期望的“Base”
ldvirtftn
案例中,您得到的程序无效,因为您忘记了ldvirtftn
需要堆栈上的对象引用,而ldftn
不需要- 按照非静态方法的惯例,将
或Base
的实际实例传递给委托构造函数,而不是Child
。您将发现它将拒绝创建委托,因为参数的数量不再匹配(null
需要一个参数,但是Action
没有参数)Method()
- 通过将
更改为简单的Action
,或通过使Action
接受参数,使参数的数量匹配。在这两种情况下,您可能很快就会发现它实现了您的期望。特别是,您会发现使用Method()
创建的委托始终会调用ldftn
,即使您是使用Base.Method()
的实例创建它的Child
string->int
函数可以用来创建“static”Func
委托和一个“instance”Func
委托。只需ldnull
(或ldstr
)+ldftn
+newobj
,使用任一委托类型即可。