简体   繁体   English

Reflection.Emit使用参数创建对象

[英]Reflection.Emit create object with parameters

I'm creating a dynamic function to create an object at runtime given an object[] of constructor params. 我正在创建一个动态函数,在给定构造函数params的object []的情况下在运行时创建一个对象。 I keep getting the generic exception 'Operation could destablise the runtime' and I can't see what I've done wrong. 我一直得到通用异常'操作可能会破坏运行时',我看不出我做错了什么。

The method works fine if the created object needs no constructor arguments - so the problem must be in the code in the for loop. 如果创建的对象不需要构造函数参数,则该方法可以正常工作 - 因此问题必须在for循环中的代码中。

The code indexes into the given object[] putting the object onto the stack after which the ctor is called and the object returned. 代码索引到给定的对象[]中,将对象放入堆栈,然后调用ctor并返回对象。

Any ideas??? 有任何想法吗???

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

Calling code 调用代码

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

It works fine for me, as long as I make all the constructor parameters object : 它对我来说很好,只要我创建所有构造函数参数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 });
}

I think the problem is that you haven't done any conversions from the objects from the array to the actual constructor types, noting that you need to consider both reference types and value-types (unbox). 我认为问题是你没有从数组中的对象到实际的构造函数类型进行任何转换,注意到你需要同时考虑引用类型和值类型(unbox)。 Like so: 像这样:

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

as a minor note: since you need to call .GetParameters() , you should not pass in the parameter length as a parameter to the method; 作为未成年人记:因为你需要调用.GetParameters()应该传递参数长度作为参数的方法; that is redundant, and could cause errors when wrong. 这是多余的,并且可能在错误时导致错误。

This then works with my exmaple: 这适用于我的exmaple:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM