通过 C# EMIT IL 创建/分配对象

问题描述 投票:0回答:2

IL 新手...尝试创建 IL:

Dest CreateInstance(Source src)
{
   Dest d = new Dest();
   d.Test = src.Test;
   return d;
}

这是我到目前为止所拥有的:

ConstructorInfo ctor = typeof(Dest).GetConstructors()[0];
DynamicMethod method = new DynamicMethod("CreateIntance", typeof(Dest),
    new Type[] { typeof(Source) });
ILGenerator gen = method.GetILGenerator();
//gen.Emit(OpCodes.Ldarg_0);// source
gen.Emit(OpCodes.Newobj, ctor);// new Created
gen.Emit(OpCodes.Ret);
CreateCtor createdCtorDelegate;
createdCtorDelegate = (CreateCtor)method.CreateDelegate(typeof(CreateCtor));

这按上面的方式运行...但是如果我取消注释 Ldarg_0,当我尝试调用委托时,我会收到“此操作可能会不稳定运行时”。

另外,我需要将测试成员复制过来吗?假设它是基本类型。

谢谢!

编辑:

源和目标以及简单的 POCO。

public class Source
{
    public string S1 { get; set; }
    public string S2 { get; set; }
    public int I1 { get; set; }
    public int I2 { get; set; }
    public string S3 { get; set; }
    public string S4 { get; set; }
    public string S5 { get; set; }
}

public class Dest
{
    public string S1 { get; set; }
    public string S2 { get; set; }
    public int I1 { get; set; }
    public int I2 { get; set; }
    public string S3 { get; set; }
    public string S4 { get; set; }
    public string S5 { get; set; }
}

编辑#2:现在,我有这个......仍然出现不稳定错误:

        ConstructorInfo ctor = typeof(Dest).GetConstructors()[0];
        DynamicMethod method = new DynamicMethod("CreateIntance", typeof(Dest),
            new Type[] { typeof(Source) });
        MethodInfo miSrc = tSource.GetProperty("S1").GetGetMethod();
        MethodInfo miDest = tDest.GetProperty("S1").GetSetMethod();

        ILGenerator gen = method.GetILGenerator();
        gen.Emit(OpCodes.Newobj, ctor);// new Created
        gen.Emit(OpCodes.Dup);
        gen.Emit(OpCodes.Ldarg_1);// source
        gen.Emit(OpCodes.Ldfld, miSrc);
        gen.Emit(OpCodes.Stfld, miDest);
        gen.Emit(OpCodes.Ret);
        CreateCtor createdCtorDelegate;
        createdCtorDelegate = (CreateCtor)method.CreateDelegate(typeof(CreateCtor));

        Dest dd = createdCtorDelegate(s);

调用createdCtorDelegate时出现异常。

编辑3:

ILSpy 显示了这一点:

.method public hidebysig static 
    class ConsoleApplication3.Dest Test (
        class ConsoleApplication3.Source s
    ) cil managed 
{
    // Method begins at RVA 0x2148
    // Code size 26 (0x1a)
    .maxstack 2
    .locals init (
        [0] class ConsoleApplication3.Dest,
        [1] class ConsoleApplication3.Dest
    )

IL_0000: nop
IL_0001: newobj instance void ConsoleApplication3.Dest::.ctor()
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: ldarg.0
IL_0009: callvirt instance string ConsoleApplication3.Source::get_S1()
IL_000e: callvirt instance void ConsoleApplication3.Dest::set_S1(string)
IL_0013: nop
IL_0014: ldloc.0
IL_0015: stloc.1
IL_0016: br.s IL_0018

IL_0018: ldloc.1
IL_0019: ret
} // end of method Program::Test

所以,我将代码调整为:

        ConstructorInfo ctor = typeof(Dest).GetConstructors()[0];
        DynamicMethod method = new DynamicMethod("CreateIntance", typeof(Dest),
            new Type[] { typeof(Source) });
        MethodInfo miSrc = tSource.GetProperty("S1").GetGetMethod();
        MethodInfo miDest = tDest.GetProperty("S1").GetSetMethod();

        ILGenerator gen = method.GetILGenerator();

        gen.Emit(OpCodes.Newobj, ctor);// new Created
        gen.Emit(OpCodes.Stloc_0);
        gen.Emit(OpCodes.Ldloc_0);
        gen.Emit(OpCodes.Ldarg_0);
        gen.Emit(OpCodes.Callvirt, miSrc);
        gen.Emit(OpCodes.Callvirt, miDest);
        gen.Emit(OpCodes.Ldloc_0);
        gen.Emit(OpCodes.Stloc_1);
        gen.Emit(OpCodes.Ldloc_1);
        gen.Emit(OpCodes.Ret);

        CreateCtor createdCtorDelegate;
        createdCtorDelegate = (CreateCtor)method.CreateDelegate(typeof(CreateCtor));

仍然崩溃:(...

c# .net reflection cil emit
2个回答
1
投票

...但是如果我取消注释 Ldarg_0 ...

您的方法应该返回一个值,这意味着当到达

ret
指令时,堆栈必须恰好包含一项。
newobj
指令将创建新对象并将对其的引用加载到堆栈中,因此,如果您向堆栈添加更多项目但在到达
ret
之前没有消耗它们,则您的代码无效。

我需要复制测试成员什么?

获得有效 IL 指令的最快方法是使用高级语言以及编译器和反编译器。 当你这样做时,你会得到这样的东西:

IL_0000: newobj instance void Dest::.ctor() //create the new object and store reference on the stack
IL_0005: dup                                //duplicate the reference on the stack
IL_0006: ldarg.1                            //load the object to copy from
IL_0007: ldfld object Source::Test          //load the value from the Source object's Test
IL_000c: stfld object Dest::Test            //save the value to the Dest object's Test
IL_0011: ret                                //one reference of the new object is still on the stack

0
投票

您的最后一个代码接近正确,您只需声明您要使用的本地代码:

ILGenerator gen = method.GetILGenerator();
gen.DeclareLocal(typeof(Dest));

还要注意最后一对

stloc.1
,
ldloc.1
是不必要的,是在 Debug 模式下编译代码的结果。检查 CIL 时,请务必在 Release 中编译代码,因为代码更短且更易于阅读。

你的第二个版本的方法也几乎是正确的,问题就在这里:

gen.Emit(OpCodes.Ldarg_1);// source

不存在“参数 1”,因为参数列表从 0 开始(对于静态方法,实例方法将

this
作为参数 0)。修复方法很简单:

gen.Emit(OpCodes.Ldarg_0);// source
© www.soinside.com 2019 - 2024. All rights reserved.