Reflection.Emit 创建带有参数的对象

发布于 2024-12-17 10:47:17 字数 3335 浏览 1 评论 0原文

我正在创建一个动态函数,以便在给定构造函数参数的 object[] 的情况下在运行时创建一个对象。我不断收到通用异常“操作可能会破坏运行时的稳定性”,但我看不出我做错了什么。

如果创建的对象不需要构造函数参数,则该方法可以正常工作 - 因此问题一定出在 for 循环的代码中。

代码索引到给定的 object[] 中,将对象放入堆栈中,然后调用 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

I'm creating a dynamic function to create an object at runtime given an object[] of constructor params. 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.

The code indexes into the given object[] putting the object onto the stack after which the ctor is called and the object returned.

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

鸢与 2024-12-24 10:47:17

只要我将所有构造函数参数设置为 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 });
}

我认为问题是您尚未完成从数组中的对象到实际构造函数类型的任何转换,请注意您需要考虑引用类型和值类型(拆箱)。像这样:

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

It works fine for me, as long as I make all the constructor parameters 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). 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; that is redundant, and could cause errors when wrong.

This then works with my 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 });
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文