3

私は次のクラスを持っています:

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>));
}

次に、このメソッドを使用して、コンストラクターごとに 1 つずつ、このクラスの 2 つのインスタンスを作成します。

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

ただし、1instanceTwo取得する代わりに、43603896を取得しています。

関連するコンストラクターでブレークポイントにヒットすると、確かに43603896がインスタンスに渡されていることが示されますが、理由がわかりません!?

4

1 に答える 1

4

まず、OpCodes.Boxここでは明らかに間違っています。オブジェクトをボックス化するのではなく、オブジェクトからボックス化を解除したいからです。

ここで、値をボックス化解除し、ボックス化解除された値への参照をスタックに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しかし、基本的に同じことを行うだけで簡単に使用できますが、1行で:

ilGen.Emit(OpCodes.Unbox_Any, paramType);
于 2016-12-16T21:14:13.760 に答える