Создать объект, зная только имя класса?
У меня есть набор классов, каждый из которых является другим strategy для выполнения той же работы.
namespace BigCorp.SuperApp
{
public class BaseClass { }
public class ClassA : BaseClass { }
public class ClassB : BaseClass { }
}
Выбор стратегии использования настраивается. Я хочу настроить только имя класса "ClassB" вместо полного имени типа "BigCorp.SuperApp.ClassB" в файле app.config.
<appConfig>
<SuperAppConfig>
<Handler name="ClassB" />
</SuperAppConfig>
</appConfig>
Однако вызовы отражения терпят неудачу, потому что они ожидают полного имени типа, особенно
Type t = Type.GetType("ClassB"); // results in t == null
BaseClass c = Activator.CreateInstance(t) as BaseClass; // fails
Как я могу заставить это работать при настройке только имени класса? Объединить пространство имен с именем класса для полного имени типа? Есть ли другой рефлекторный вызов, который работает?
Если вы считаете, что это бесполезно, и я должен ожидать, что конфигурация будет содержать полное имя типа, я открыт для этого решения! Просто предоставьте обоснование, чтобы убедить меня.
(я не загружаю тип из-за пределов этой сборки/пространства имен)
Ответы
Ответ 1
Поскольку вы знаете, что все классы будут поступать из одного и того же пространства имен, настройте его один раз и используйте это:
<appConfig>
<SuperAppConfig handlerNamespace="BigCorp.SuperApp">
<Handler class="ClassB" />
</SuperAppConfig>
</appConfig>
Изменить: Я изменил имя на класс, чтобы лучше обозначить значение этого атрибута.
Ответ 2
Используйте либо имя с именем сборки, либо получите сборку и используйте Assembly.GetType(name)
. В этом случае, поскольку вам нужны типы в файле конфигурации, сборка является допустимым способом - но поскольку вы знаете, что все ваши типы находятся в одной сборке:
Assembly assembly = typeof(SomeKnownType).Assembly; // in the same assembly!
Type type = assembly.GetType(name); // full name - i.e. with namespace (perhaps concatenate)
object obj = Activator.CreateInstance(type);
Статический Type.GetType(string)
имеет правила зондирования, которые часто вызывают путаницу... он смотрит на вызывающую сборку и несколько сборок системы - но не все загруженные сборки.
Ответ 3
(I will not be loading a type from outside this assembly/namespace)
из-за вышеприведенной строки, можно с уверенностью предположить, что вы знаете, что такое пространство имен. Не могли бы вы сделать что-то вроде:
Type t = Type.GetType("Namespace." + className);
BaseClass c = Activator.CreateInstance(t) as BaseClass;
Если вы ожидаете, что сможете добавить дополнительные классы стратегий, которые будут загружены в будущем, возможно, через дополнительную сборку, вам нужно будет полностью квалифицировать свое имя класса. Это рекомендуется в любом случае, поскольку вы сможете обеспечить расширенную расширяемость для своего приложения.
Ответ 4
Я собираюсь с полным именем типа в конфигурации приложения. Ниже приведен немного более полный, но еще тривиальный пример
<SuperAppConfig>
<ObjectConfig provider="BigCorp.SuperApp.ClassA">
<add name="one" />
<add name="two" />
</ObjectConfig>
</SuperAppConfig>
И класс factory, который на самом деле создает этот
private static Assembly a = typeof(IFactoryObject).Assembly;
public static IFactoryObject CreateObject(String providerName)
{
Type t = a.GetType(providerName)
IFactoryObject o = Activator.CreateInstance(t) as IFactoryObject;
return o;
}
Ответ 5
BaseClass c = Activator.CreateInstance(t) as BaseClass; // fails
Возможно также из-за того, что CreateInstance не возвращает экземпляр BaseClass, а не экземпляр BaseClass, завернутый в ObjectHandle.
Включение в ваш BaseClass после использования метода UnWrap.