7

我发现了这个,但尝试使用它并失败了。

如何使用反射创建对象并通过将其放入委托中使其快速?

        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

4 回答 4

12

如果您有权访问 .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();        
}

很容易扩展它以使用特定的构造函数、传递参数或添加构造函数后属性绑定;演员表,转换等(请参阅此相关答案)。如果您有特定的场景,我会很乐意添加一个示例。

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

于 2010-01-12T18:47:43.950 回答
5

尝试这个 -

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);
}
于 2010-01-12T18:39:30.817 回答
1

Generic method for constructing delegates, calling constructor directly. Automatically searches constructor in given type with signature of given delegate type and constructs delegate of that type. Code here:

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

is part of Yappi project's sources. Using it you can construct delegate calling any constructor of given type, including constructor with parameters (except ref and out parameters).

Sample usage:

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

After construction of delegate, store it somewhere in static dictionary or in static field of class with generic parameter. Don't construct new delegate each time. Use one delegate for constructing multiple instances of given type.

于 2011-07-30T13:36:14.847 回答
1

构造函数没有参数,因此您不应在堆栈上加载参数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);
    }
}
于 2010-01-12T18:39:54.263 回答