ответ
Редактировать: Обратите внимание, что этот ответ был дан до того, как вопрос был полностью изменен при редактировании. Из-за этого он теперь относится к вещам, которые присутствовали только в вопросе, как изначально указано. Прошу прощения за все «болтающиеся указатели». :-)
Короткий ответ:
С кодом вы в курсе, я не вижу альтернативы литья до IFoo<T>
. Если вы этого не сделаете, компилятор даст предупреждение (по крайней мере, на моей машине).
Более сложный ответ:
ли на самом деле должны быть таким образом ваш код? В частности, вам нужно, чтобы вопрос был поставлен в первую очередь?
Я предполагаю, что вы собираетесь назвать свой фабричный метод более или менее, как это:
var stringFoo = FooFactory.CreateFoo<string>();
Вы должны предоставить параметр шаблона (string
в данном случае) в явном виде, поскольку она не может быть получена из любого аргумента метода (в этом случае, потому что их вообще нет вообще). Очевидно, что заводский метод вернет IFoo<string>
.
Теперь, так как вы должны явно указать тип во время выполнения, вы могли бы точно так же написать:
var stringFoo = StringFoo.Create();
и, следовательно, имеют фабричный метод внутри StringFoo
, как это, что безусловно делает очевидным :
public class StringFoo : IFoo<string>
{
...
public static StringFoo Create() // or alternatively, return an IFoo<string>
{
return new StringFoo();
}
}
применяя эту модель к другим IFoo<T>
реализации тоже, это сэкономит вам if
цепи или switch
блока внутри FooFactory.CreateFoo<T>
, сделать код проще и избавиться от необходимости бросать (что вас беспокоит).
Не поймите меня неправильно, я знаю, что фабричные методы, поддерживающие более одного типа объектов , являются полезными в некоторых случаях; но, похоже, в вашем случае это вызывает больше проблем, чем того стоит.
P.S .: Вы могли бы найти один аспект некоторых IoC контейнеров интересных.Обычно их необходимо настроить, и это включает процесс, в котором вы регистрируете конкретные типы (например, классы реализации) для абстрактных интерфейсов; например (здесь с помощью Autofac):
var builder = new ContainerBuilder();
builder.RegisterType<StringFoo>().As<IFoo<string>>();
Позже, вы можете запросить экземпляр объекта абстрактного типа:
using (var container = builder.Build())
{
var stringFoo = container.Resolve<IFoo<string>>();
...
}
Метод Resolve
интересная часть. Вы предоставляете его абстрактным типом и используя зарегистрированные типы, он вернет конкретный объект типа StringFoo
. Посмотрите на него, если вам это не кажется излишним! :-)
Можете ли вы описать проблему вы решаете с этим механизмом? Скорее всего, есть более ясный способ приблизиться к нему.
Редактировать
И да, код запахи. Вы оставили комнату открытой для любого типа, за исключением того, что вы затем ограничиваете ее до одного типа и генерируете исключение во время выполнения. Почему в этом случае есть параметр типа?
Нет, я нет. Я не понимаю полезности абстрактной фабрики, которая предоставляет общий интерфейс, но в конечном итоге создает только один конкретный конкретный тип. То, что вы описали, является решением проблемы, которую вы еще не описали. Скажите нам, что это за проблема, а не ваше предвзятое представление о том, как ее решить, и мы сможем лучше вам помочь. –
Технология в вакууме часто приводит к решениям в поисках проблемы. Без определения ситуаций, решаемых этим механизмом, у вас нет критериев, по которым вы можете сделать оценочное суждение. Если ваш вопрос был больше, чтобы определить, является ли этот подход жизнеспособным решением * проблемы *, это другое. Я лично не вижу использования для универсальной фабрики, которая не является общей и вызывает исключения во время выполнения, когда они используются как таковые. Это как сделать машину, которая не начнется, если вы не едете в супермаркет. –
Вы могли бы попробовать что-то вроде этого ...
public static class FooFactory
{
private static readonly Dictionary<Type, Type> FooTypesLookup;
static FooFactory()
{
FooTypesLookup = (from type in typeof(FooFactory).Assembly.GetExportedTypes()
let fooInterface =
type.GetInterfaces().FirstOrDefault(
x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IFoo<>))
where fooInterface != null
let firstTypeArgument = fooInterface.GetGenericArguments().First()
select new { Type = type, TypeArgument = firstTypeArgument })
.ToDictionary(x => x.TypeArgument, x => x.Type);
}
public static IFoo<T> CreateFoo<T>()
{
var genericArgumentType = typeof(T);
Type closedFooType;
return FooTypesLookup.TryGetValue(genericArgumentType, out closedFooType)
? (IFoo<T>) Activator.CreateInstance(closedFooType)
: null;
}
}
Или еще лучше, ввести свой любимый IoC контейнер (Виндзор, структура карты, и т.д.) и регистрировать все типы, которые реализуют IFoo там и затем разрешите их, когда это необходимо вместо вызова Activator.CreateInstance.
Цель для меня состояла в том, чтобы не использовать Activator.CreateInstance(). – Merritt
Я использовал бы переключатель вместо цепочки if в методе 'CreateFoo(). –
ANeves