#c# #lambda #constructor #func #inherited
#c# #лямбда #конструктор #функция #унаследованный
Вопрос:
Как мы знаем, вы можете указать на конструктор как 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
.
Ответ №1:
Вы можете использовать деревья выражений для построения и компиляции делегата, который создаст ваш производный тип:
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();
Когда вы кэшируете этот делегат, вы получите максимальную производительность.
Комментарии:
1. кстати: это именно то, что фреймворки внедрения зависимостей сделают для вас. Большинство фреймворков допускают регистрацию следующего синтаксиса:
container.Register(typeof(MyBase), derived)
и вы можете получить новый экземпляр следующим образом:container.GetInstance<MyBase>()
и вуаля.
Ответ №2:
Демонстрирует ли эта программа желаемое поведение?
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
, не соответствует общим ограничениям. Итак, это приведет к сбою во время выполнения, а не во время компиляции, но я предполагаю, что это компромисс, который вам нужно сделать, когда вы имеете дело с обобщениями динамически, подобными этому.
Комментарии:
1. Я не знаю, что такое «Производный», кроме System.Type.
2. @Seb: Обновил ответ, чтобы он создавал универсальный метод для данного
Type
.3. Хм, «getConstructor. Invoke» в основном похож на Activator. CreateInstance и возвращает объект. Я хочу сослаться на constructor-lambda для повышения производительности, которой эквивалент на основе отражения не совсем обладает. Если это невозможно сделать, то это правильный ответ, и ваше решение — самое близкое, которое можно получить.
4. @Seb: обратите внимание, что
getConstructor.Invoke
возвращает универсальный конструктор, но не создает объект ; объект создается сконструированным универсальным вызовом (поэтому там не должно быть отражения). Вы должны легко иметь возможность кэшировать сгенерированные конструкторы для каждого типа.5. Это действительно работает? Я не думаю, что
getConstructor.Invoke(null, null)
возвращаетFunc<T>
, а просто что-то, что наследуется отMyObject
.