Reflection.Emit создать объект с параметрами - PullRequest
4 голосов
/ 22 ноября 2011

Я создаю динамическую функцию для создания объекта во время выполнения, учитывая объект [] параметров конструктора.Я продолжаю получать общее исключение «Операция может привести к дестабилизации среды выполнения», и я не вижу, что я сделал неправильно.

Метод работает нормально, если созданный объект не нуждается в аргументах конструктора - поэтому проблема должна бытьв коде в цикле for.

Код индексирует данный объект [], помещая объект в стек, после чего вызывается ctor и возвращается объект.

Есть идеи ???

internal static Func<object[], object> CreateObjectFactoryMethodWithCtorParams(ConstructorInfo ctor, int ctorArgsLength)
    {
        Func<object[], object> factoryMethod = null;
        if (ctor != null)
        {
            var dm = new DynamicMethod(string.Format("_CreationFacotry_{0}", Guid.NewGuid()), typeof(object), new Type[] { typeof(object[])}, true);
            var il = dm.GetILGenerator();
            il.DeclareLocal(typeof(int));
            il.DeclareLocal(typeof(object));

            il.BeginExceptionBlock();

            il.Emit(OpCodes.Ldc_I4_0); // [0]
            il.Emit(OpCodes.Stloc_0); //[nothing]

            for (int i = 0; i < ctorArgsLength; i++)
            {
                EmitInt32(il, i); // [args][index]
                il.Emit(OpCodes.Stloc_0); // [args][index]
                il.Emit(OpCodes.Ldarg_0); //[args]
                EmitInt32(il, i); // [args][index]
                il.Emit(OpCodes.Ldelem_Ref); // [item-in-args-at-index]
            }
            il.Emit(OpCodes.Newobj, ctor); //[new-object]
            il.Emit(OpCodes.Stloc_1); // nothing

            il.BeginCatchBlock(ExceptionType); // stack is Exception
            il.Emit(OpCodes.Ldloc_0); // stack is Exception, index
            il.EmitCall(OpCodes.Call, EmitGeneratorType.GetMethod("ThrowFactoryException"), null);
            il.EndExceptionBlock();

            il.Emit(OpCodes.Ldloc_1); //[new-object]
            il.Emit(OpCodes.Ret);
            factoryMethod = (Func<object[], object>)dm.CreateDelegate(typeof(Func<object[], object>));
        }
        else
        {
            throw new EmitGeneratorException("Cannot create instance factory for a null ctor instance");
        }
        return factoryMethod;
    }

        private static void EmitInt32(ILGenerator il, int value)
        {
            switch (value)
            {
                case -1: il.Emit(OpCodes.Ldc_I4_M1); break;
                case 0: il.Emit(OpCodes.Ldc_I4_0); break;
                case 1: il.Emit(OpCodes.Ldc_I4_1); break;
                case 2: il.Emit(OpCodes.Ldc_I4_2); break;
                case 3: il.Emit(OpCodes.Ldc_I4_3); break;
                case 4: il.Emit(OpCodes.Ldc_I4_4); break;
                case 5: il.Emit(OpCodes.Ldc_I4_5); break;
                case 6: il.Emit(OpCodes.Ldc_I4_6); break;
                case 7: il.Emit(OpCodes.Ldc_I4_7); break;
                case 8: il.Emit(OpCodes.Ldc_I4_8); break;
                default:
                    if (value >= -128 && value <= 127)
                    {
                        il.Emit(OpCodes.Ldc_I4_S, (sbyte)value);
                    }
                    else
                    {
                        il.Emit(OpCodes.Ldc_I4, value);
                    }
                    break;
            }
        }

Телефонный код

    Func<object[], object> factoryFunction = GetFunction(someCtor, new object[] { arg1, arg2});
var obj = factoryFunction(new object[] {new SomeClass, "A String" }); //input ctor args

1 Ответ

5 голосов
/ 22 ноября 2011

Это прекрасно работает для меня, пока я задаю все параметры конструктора object:

class SomeClass {
    public SomeClass(object s, object t) { }
}
static void Main()
{
    var someCtor = typeof(SomeClass).GetConstructors()[0];
    Func<object[], object> factoryFunction = CreateObjectFactoryMethodWithCtorParams(someCtor, someCtor.GetParameters().Length);
    var obj = factoryFunction(new object[] {"A String", 123 });
}

Я думаю, проблема в том, что вы не сделали никаких преобразований из объектов из массивак фактическим типам конструктора, отметив, что вам нужно учитывать как ссылочные типы, так и типы значений (unbox).Вот так:

var parameters = ctor.GetParameters();
for (int i = 0; i < parameters.Length ; i++)
{
    EmitInt32(il, i); // [index]
    il.Emit(OpCodes.Stloc_0); // [nothing]
    il.Emit(OpCodes.Ldarg_0); //[args]
    EmitInt32(il, i); // [args][index]
    il.Emit(OpCodes.Ldelem_Ref); // [item-in-args-at-index]
    var paramType = parameters[i].ParameterType;
    if (paramType != typeof(object))
    {
        il.Emit(OpCodes.Unbox_Any, paramType); // same as a cast if ref-type
    }
}
il.Emit(OpCodes.Newobj, ctor); //[new-object]
il.Emit(OpCodes.Stloc_1); // nothing

как второстепенное примечание: поскольку вам нужно вызвать .GetParameters(), вы должны , а не передать длину параметра в качестве параметра методу;это избыточно и может привести к ошибкам в случае ошибки.

Это тогда работает с моим примером:

class SomeClass {
    public SomeClass(string s, int t) { }
}
static void Main()
{
    var someCtor = typeof(SomeClass).GetConstructors()[0];
    Func<object[], object> factoryFunction = CreateObjectFactoryMethodWithCtorParams(someCtor);
    var obj = factoryFunction(new object[] {"A String", 123 });
}
...