使用Delegate调用构造函数

时间:2010-01-12 18:27:22

标签: c# reflection delegates reflection.emit

我找到this但尝试使用它并失败了。

如何使用反射创建对象并通过将其置于委托中来加快速度?

        DynamicMethod dm = new DynamicMethod("MyCtor", t, new Type[] { });            
        var ctor = t.GetConstructor(new Type[] { });
        ILGenerator ilgen = dm.GetILGenerator();
        ilgen.Emit(OpCodes.Ldarg_0);
        ilgen.Emit(OpCodes.Newobj, ctor);
        ilgen.Emit(OpCodes.Ret);
        var d = (Func<T>)dm.CreateDelegate(t);
        dm.Invoke(null, new object[] { });

在把它删除之前我试图至少调用它,当我在上面做的时候我得到了错误

An unhandled exception of type 'System.Reflection.TargetInvocationException' occurred in mscorlib.dll

附加信息:调用目标引发了异常。

如果我调用d()而不是我得到异常

An unhandled exception of type 'System.ArgumentException' occurred in mscorlib.dll

Additional information: Type must derive from Delegate.

如何将一个无参数构造函数放入委托并调用它?

4 个答案:

答案 0 :(得分:11)

如果您可以访问.NET 3.5(使用Func<T>建议),您可能会发现ExpressionILGenerator更容易:

class Foo { }
static void Main() {
    Func<Foo> func = GetCtor<Foo>(); // cache this somewhere!
    Foo foo = func();
}
static Func<T> GetCtor<T>() {
    Type type = typeof(T);
    Expression body = Expression.New(type);
    return Expression.Lambda<Func<T>>(body).Compile();        
}

很容易扩展它以使用特定的构造函数,传递参数或添加post-constructor属性绑定;演员,转换等(见this related answer)。如果您有特定的场景,我很乐意添加一个示例。

另请注意,您应该缓存并重新使用任何此类构造函数 - 否则您将失去优势(即不要重新创建每次调用的委托)。

答案 1 :(得分:4)

试试这个 -

Action myCtor = CreateCtor(t, Type.EmptyTypes, typeof(Action));

public static Delegate CreateCtor(Type type, Type[] parameterTypes, Type delegateType, string typeParameterName)
{
    var ctorInfo = type.GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, parameterTypes, null);
    if (ctorInfo == null)
    {
        string parameterString = string.Empty;
        if(parameterTypes.Length > 0)
        {
            string[] parameterStrings = new string[parameterTypes.Length];
            for(int i = 0; i < parameterTypes.Length; ++i)
            {
                parameterStrings[i] = parameterTypes[i].ToString();
            }
            parameterString = string.Join(",", parameterStrings);
        }
        throw new ArgumentException(string.Format("Type '{0}' does not define .ctor({1}).", type, parameterString), typeParameterName);
    }

    bool isVisible = type.IsVisible && (ctorInfo.IsPublic && !ctorInfo.IsFamilyOrAssembly);

    DynamicMethod dynamicCtor = new DynamicMethod(Guid.NewGuid().ToString("N"), type, parameterTypes, ctorInfo.Module, !isVisible);
    var il = dynamicCtor.GetILGenerator();
    for (int i = 0; i < parameterTypes.Length; ++i)
    {
        switch (i)
        {
            case 0: il.Emit(OpCodes.Ldarg_0); break;
            case 1: il.Emit(OpCodes.Ldarg_1); break;
            case 2: il.Emit(OpCodes.Ldarg_2); break;
            case 3: il.Emit(OpCodes.Ldarg_3); break;
            default: il.Emit(OpCodes.Ldarg, i); break;
        }
    }
    il.Emit(OpCodes.Newobj, ctorInfo);
    il.Emit(OpCodes.Ret);
    return dynamicCtor.CreateDelegate(delegateType);
}

答案 2 :(得分:0)

构造函数没有参数,所以你不应该在堆栈ilgen.Emit(OpCodes.Ldarg_0)上加载参数:

class Program
{
    static void Main()
    {
        var t = typeof(Program);
        var dm = new DynamicMethod("MyCtor", t, new Type[0], t.Module);
        var ctor = t.GetConstructor(new Type[0]);
        ILGenerator ilgen = dm.GetILGenerator();
        ilgen.Emit(OpCodes.Newobj, ctor);
        ilgen.Emit(OpCodes.Ret);
        var del = (Func<Program>)dm.CreateDelegate(typeof(Func<Program>));
        var instance = del();
        Console.WriteLine(instance);
    }
}

答案 3 :(得分:0)

构造委托的通用方法,直接调用构造函数。 使用给定委托类型的签名自动搜索给定类型的构造函数,并构造该类型的委托。代码在这里:

/// <summary>
/// Reflective object construction helper.
/// All methods are thread safe.
/// </summary>
public static class Constructor
{
    /// <summary>
    /// Searches an instanceType constructor with delegateType-matching signature and constructs delegate of delegateType creating new instance of instanceType.
    /// Instance is casted to delegateTypes's return type. 
    /// Delegate's return type must be assignable from instanceType.
    /// </summary>
    /// <param name="delegateType">Type of delegate, with constructor-corresponding signature to be constructed.</param>
    /// <param name="instanceType">Type of instance to be constructed.</param>
    /// <returns>Delegate of delegateType wich constructs instance of instanceType by calling corresponding instanceType constructor.</returns>
    public static Delegate Compile(Type delegateType,Type instanceType)
    {
        if (!typeof(Delegate).IsAssignableFrom(delegateType))
        {
            throw new ArgumentException(String.Format("{0} is not a Delegate type.",delegateType.FullName),"delegateType");
        }
        var invoke = delegateType.GetMethod("Invoke");
        var parameterTypes = invoke.GetParameters().Select(pi => pi.ParameterType).ToArray();
        var resultType = invoke.ReturnType;
        if(!resultType.IsAssignableFrom(instanceType))
        {
            throw new ArgumentException(String.Format("Delegate's return type ({0}) is not assignable from {1}.",resultType.FullName,instanceType.FullName));
        }
        var ctor = instanceType.GetConstructor(
            BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, parameterTypes, null);
        if(ctor == null)
        {
            throw new ArgumentException("Can't find constructor with delegate's signature","instanceType");
        }
        var parapeters = parameterTypes.Select(Expression.Parameter).ToArray();

        var newExpression = Expression.Lambda(delegateType,
            Expression.Convert(Expression.New(ctor, parapeters), resultType),
            parapeters);
        var @delegate = newExpression.Compile();
        return @delegate;
    }
    public static TDelegate Compile<TDelegate>(Type instanceType)
    {
        return (TDelegate) (object) Compile(typeof (TDelegate), instanceType);
    }
}

Yappi项目来源的一部分。使用它,您可以构造委托调用给定类型的任何构造函数,包括带参数的构造函数(ref和out参数除外)。

样本用法:

var newList = Constructor.Compile<Func<int, IList<String>>>(typeof (List<String>));
var list = newList(100);

构建委托后,将其存储在静态字典中或使用泛型参数的类的静态字段中。不要每次都构造新的委托。使用一个委托来构造给定类型的多个实例。