C #: Func с конструктором унаследованного типа - PullRequest
3 голосов
/ 30 мая 2011

Как мы знаем, вы можете указать на конструктор как Func<T>, например:

Func<MyObject> constructor = () => new MyObject();
var newObject = constructor();

Но есть ли способ сделать конструктором для объекта, который, как вы знаете, наследует от MyObject , но вы не знаете его точный тип?

Type inheritedObjectType = obj; // Parameter
Func<MyObject> constructor = () => new MyObject(); // as inheritedObjectType
var newInheritedObject = constructor; // Should now be of the inherited type

Ответ с использованием Activator или чем-либо, возвращающим Object, не вариант.

Редактировать : Я не знаю, к какому типу относится производный тип во время компиляции. У меня есть только System.Type.

Ответы [ 2 ]

7 голосов
/ 30 мая 2011

Вы можете использовать деревья выражений для построения и компиляции делегата, который создаст ваш производный тип:

Func<TBase> CreateDelegate<TBase>(Type derived)
{
    var ctor = derived.GetConstructor(Type.EmptyTypes);

    if (ctor == null)
    {
        throw new ArgumentException("D'oh! No default ctor.");
    }

    var newExpression = Expression.Lambda<Func<TBase>>(
        Expression.New(ctor, new Expression[0]), 
        new ParameterExpression[0]);

    return newExpression.Compile();
}

Вы можете просто назвать это следующим образом:

Func<MyBase> create = CreateDelegate<MyBase>(typeof(Derived));

MyBase instance = create();

Когда вы кешируете этот делегат, вы получите максимальную производительность.

2 голосов
/ 30 мая 2011

Эта программа демонстрирует желаемое поведение?

class MyObject{}
class Derived : MyObject {}
internal class Program
{

    public static void Main()
    {
        // the type you want to construct
        Type type = typeof (Derived);

        MethodInfo getConstructor = MakeConstructorGetter(type);
        Func<MyObject> constructor = (Func<MyObject>)getConstructor.Invoke(null, null);

        var obj = constructor();
        Console.WriteLine(obj.GetType());
    }

    private static MethodInfo MakeConstructorGetter(Type type)
    {
        MethodInfo mi = typeof(Program).GetMethod("GetObjectConstructor", BindingFlags.Static | BindingFlags.NonPublic);
        var getConstructor = mi.MakeGenericMethod(type);
        return getConstructor;
    }

    private static Func<T> GetObjectConstructor<T>() where T : MyObject, new()
    {
        return () => new T();
    }
}

MakeConstructorGetter вызовет исключение, если тип, описанный Type, не соответствует общим ограничениям. Таким образом, это потерпит неудачу во время выполнения, а не во время компиляции, но я полагаю, что это компромисс, который вы должны сделать при работе с генериками динамически, как это.

...