Создать универсальный класс с внутренним конструктором
Можно ли построить объект с его внутренним конструктором в универсальном методе?
public abstract class FooBase { }
public class Foo : FooBase {
internal Foo() { }
}
public static class FooFactory {
public static TFooResult CreateFoo<TFooResult>()
where TFooResult : FooBase, new() {
return new TFooResult();
}
}
FooFactory
находится в той же сборке, что и Foo
, Классы вызывают фабричный метод следующим образом:
var foo = FooFactory.CreateFoo<Foo>();
Они получают ошибку во время компиляции:
'Foo' должен быть неабстрактным типом с открытым конструктором без параметров, чтобы использовать его в качестве параметра 'TFooType' в универсальном типе или методе 'FooFactory. CreateFoo()'
Есть ли способ обойти это?
Я также попробовал:
Activator.CreateInstance<TFooResult>();
Это вызывает ту же ошибку во время выполнения.
4 ответа
Вы можете удалить new()
ограничение и возврат:
//uses overload with non-public set to true
(TFooResult) Activator.CreateInstance(typeof(TFooResult), true);
хотя клиент мог сделать это тоже. Это, однако, подвержено ошибкам во время выполнения.
Это сложная проблема, которую можно решить безопасным способом, поскольку язык не допускает объявления абстрактного конструктора.
public class GenericFactory
{
public static T Create<T>(object[] args)
{
var types = new Type[args.Length];
for (var i = 0; i < args.Length; i++)
types[i] = args[i].GetType();
return (T)typeof(T).GetConstructor(types).Invoke(args);
}
}
Может быть несколько обходных путей, как показано ниже, но я не думаю, что вы хотите пойти по этому пути!
Поместите оператор switch в Factory, который создаст экземпляр на основе типа параметра типа.
Каждая конкретная реализация FooBase будет регистрироваться в FooFactory, передавая фабричный метод для самостоятельного создания. Так что FooFactory будет использовать внутренний словарь
Расширение на аналогичную строку, за исключением отображения между параметром типа и конкретной реализацией, будет внешним кодом (файл XML, конфигурация и т. Д.). Контейнеры IOC/DI также могут помочь здесь.
Аргумент типа должен иметь открытый конструктор без параметров. При использовании вместе с другими ограничениями ограничение new() должно указываться последним.
http://msdn.microsoft.com/en-us/library/d5x73970.aspx
edit: так что нет, если вы используете ограничение new(), вы не можете передать этот класс, если вы не используете ограничение new(), вы можете попробовать использовать отражение для создания нового экземпляра
public static TFooResult CreateFoo<TFooResult>()
where TFooResult : FooBase//, new()
{
return (TFooResult)typeof(TFooResult).GetConstructor(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance, null, new Type[] {}, null).Invoke(new object[]{});
//return new TFooResult();
}