Создать объект, зная только имя класса?

У меня есть набор классов, каждый из которых является другим 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.