c#发出动态方法委托加载参数化构造函数问题

时间:2010-02-28 23:47:38

标签: c# delegates constructor reflection.emit dynamic-method

我正在尝试通过发出一个动态方法来创建构造函数的委托表示,该方法必须匹配这个非常“松散类型”的签名,以便它可以与任何类型的参数化构造函数一起使用:

public delegate Object ParamsConstructorDelegate(params object[] parameters);

并且创建委托的代码看起来像(注意这是针对Silverlight的)

public static ParamsConstructorDelegate CreateDelegate(ConstructorInfo constructor)
    {
        Guard.ArgumentNotNull(constructor, "constructor");
        Guard.ArgumentValue(constructor.GetParameters().Length == 0, MUSTBE_PARAMETERIZED_CONSTRUCTOR);

        var _argumentTypes = new Type[] { typeof(object[]) };
        var _parameters = constructor.GetParameters();
        var _parameterTypes = _parameters.Select((p) => p.ParameterType).ToArray();

        var _sourceType = constructor.DeclaringType;
        var _method = new DynamicMethod(constructor.Name, _sourceType, _argumentTypes);
        var _gen = _method.GetILGenerator();

        for (var _i = 0; _i < _parameters.Length; _i++)
        {
            if (_parameters[_i].IsOut || _parameterTypes[_i].IsByRef)
            {
                if (_i < 128)
                {
                    _gen.Emit(OpCodes.Ldarga_S, (byte)_i);
                }
                else
                    _gen.Emit(OpCodes.Ldarga, _i);
            }
            else
            {
                switch (_i)
                {
                    case 0:
                        _gen.Emit(OpCodes.Ldarg_0, _i);
                        break;
                    case 1:
                        _gen.Emit(OpCodes.Ldarg_1, _i);
                        break;
                    case 2:
                        _gen.Emit(OpCodes.Ldarg_2, _i);
                        break;
                    case 3:
                        _gen.Emit(OpCodes.Ldarg_3, _i);
                        break;
                    default:
                        if (_i < 128)
                            _gen.Emit(OpCodes.Ldarg_S, (byte)_i);
                        else
                            _gen.Emit(OpCodes.Ldarg, _i);
                        break;
                }
            }
        }
        _gen.Emit(OpCodes.Newobj, constructor);
        _gen.Emit(OpCodes.Ret); ;

        return (ParamsConstructorDelegate)_method.CreateDelegate(typeof(ParamsConstructorDelegate));
    }

现在,我得到一个“操作可能破坏运行时的稳定性”。验证异常,显然IL错了,所以我希望有人可以纠正我。

由于

2 个答案:

答案 0 :(得分:6)

我可以看到两个问题;首先,_iLdarg_0个案例Ldarg_3不需要ref(隐含)。其次 - 你的代表只有一个arg(数组)。您将需要从数组中获取项目并进行强制转换 - 如下所示(仅处理按值传递;对于out / using System; using System.Reflection.Emit; public delegate object ParamsConstructorDelegate(params object[] parameters); public class Foo { string s; int i; float? f; public Foo(string s, int i, float? f) { this.s = s; this.i = i; this.f = f; } } static class Program { static void Main() { var ctor = Build(typeof(Foo)); Foo foo1 = (Foo)ctor("abc", 123, null); Foo foo2 = (Foo)ctor(null, 123, 123.45F); } static ParamsConstructorDelegate Build(Type type) { var mthd = new DynamicMethod(".ctor", type, new Type[] { typeof(object[]) }); var il = mthd.GetILGenerator(); var ctor = type.GetConstructors()[0]; // not very robust, but meh... var ctorParams = ctor.GetParameters(); for (int i = 0; i < ctorParams.Length; i++) { il.Emit(OpCodes.Ldarg_0); switch (i) { 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: il.Emit(OpCodes.Ldc_I4, i); break; } il.Emit(OpCodes.Ldelem_Ref); Type paramType = ctorParams[i].ParameterType; il.Emit(paramType.IsValueType ? OpCodes.Unbox_Any : OpCodes.Castclass, paramType); } il.Emit(OpCodes.Newobj, ctor); il.Emit(OpCodes.Ret); return (ParamsConstructorDelegate) mthd.CreateDelegate(typeof(ParamsConstructorDelegate)); } } ,您必须定义本地并使用stloc / ldloca / etc):

static object CreateFoo(object[] vals)
{
    return new Foo((string)vals[0], (int)vals[1], (float?)vals[2]);
}

有关信息 - 我很懒 - 如果我想知道要写什么IL我用C#写它然后将它加载到反射器中。例如,要做到这一点,我写了一个方法:

{{1}}

并从那里撤消

答案 1 :(得分:1)

我发现很难理解错误消息“操作可能会破坏运行时的稳定性”意味着什么时候使用Reflection.Emit - CLR在这里没有提供太多有用的信息。可以用来获取有关该问题的更多信息的一个技巧是修改代码,以便在调试模式下运行时将代码发送到某些临时程序集(除了发出动态委托)。

然后您可以使用peverify工具(来自Visual Studio命令行),它通常会为您提供有关生成的IL代码问题的更多信息:

 > peverify assembly.dll

您需要使用AssemblyBuilderModuleBuilder等类来生成程序集。然后,您可以运行核心部分(使用ILGenerator)两次来生成动态委托(用于实际运行)和临时程序集(用于调试)。我相信peverify会为您提供更好的信息。