ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

为什么我无法在C#中使用IL生成来创建对象的实例?

2019-10-26 12:06:19  阅读:176  来源: 互联网

标签:reflection-emit dynamicmethod c net


我有以下课程:

private sealed class Person
{
    public string Name { get; }
    public int Age { get; }

    public Person(string name)
    {
        Name = name;
    }

    public Person(string name, int age)
    {
        Name = name;
        Age = age;
    }
}

以及以下用于动态创建ConstructorInfo实例的方法:

public static Func<object[], T> GetBuilder<T>(ConstructorInfo constructor)
{
    var type = constructor.ReflectedType;    
    var ctorParams = constructor.GetParameters();

    var dynamicMethod = new DynamicMethod("Create_" + constructor.Name, type, new[] { typeof(object[]) }, type, true);
    var ilGen = dynamicMethod.GetILGenerator();

    /*
     * Cast each argument of the input object array to the appropriate type
     * The order of objects should match the order set by the Ctor
     * It is also assumed the length of object array args is same length as Ctor args. 
     * Exceptions for the delegate that mean the above weren't satisfied: 
     * InvalidCastException, IndexOutOfRangeException
     */
    for (var i = 0; i < ctorParams.Length; i++)
    {
        // Push Object array
        ilGen.Emit(OpCodes.Ldarg_0);

        // Push the index to access
        ilGen.Emit(OpCodes.Ldc_I4, i);

        // Push the element at the previously loaded index
        ilGen.Emit(OpCodes.Ldelem_Ref);

        // Cast the object to the appropriate Ctor Parameter Type
        var paramType = ctorParams[i].ParameterType;
        ilGen.Emit(paramType.IsValueType ? OpCodes.Box : OpCodes.Castclass, paramType);
    }

    // Call the Ctor, all values on the stack are passed to the Ctor
    ilGen.Emit(OpCodes.Newobj, constructor);
    // Return the new object
    ilGen.Emit(OpCodes.Ret);

    // Create delegate from our IL, cast and return
    return (Func<object[], T>)dynamicMethod.CreateDelegate(typeof(Func<object[], T>));
}

然后,我使用该方法为每个构造函数创建该类的两个实例:

var ctorOne = typeof(Person).GetConstructors(BindingFlags.Public | BindingFlags.Instance)[0];
var instanceBuilderOne = GetBuilder<Person>(publicCtor[0]);
var instanceOne = instanceBuilderOne(new object[] { "Foo"});
instanceOne.Name.Dump(); // is "Foo"

var ctorTwo = typeof(Person).GetConstructors(BindingFlags.Public | BindingFlags.Instance)[1];
var instanceBuilderTwo = GetBuilder<Person>(publicCtor[1]);
var instanceTwo = instanceBuilderTwo(new object[] { "Bar", 1});
instanceTwo.Name.Dump(); // is "Bar"
instanceTwo.Age.Dump(); // is 43603896

但是对于instanceTwo而不是1我得到43603896.

在相关构造函数中命中断点确实确实显示43603896被传递给实例,但我不知道为什么!

解决方法:

首先,这里的OpCodes.Box显然是错误的,因为您想将int从对象中取消装箱,而不是将其装箱.

现在,OpCodes.Unbox的作用是将值取消装箱并将对未装箱值的引用推入堆栈.该参考是您看到的而不是“ 1”.如果要使用OpCodes.Unbox,正确的方法是:

if (paramType.IsValueType) {
     ilGen.Emit(OpCodes.Unbox, paramType);
     ilGen.Emit(OpCodes.Ldobj, paramType);
}
else {
      ilGen.Emit(OpCodes.Castclass, paramType);
}

但是使用OpCodes.Unbox_Any更容易,基本上可以做到这一点,但只需要一行:

ilGen.Emit(OpCodes.Unbox_Any, paramType);

标签:reflection-emit,dynamicmethod,c,net
来源: https://codeday.me/bug/20191026/1936448.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有