Создать объект, зная только имя класса? - PullRequest
15 голосов
/ 17 ноября 2008

У меня есть набор классов, каждый из которых имеет свою стратегию для выполнения одной и той же работы.

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

Как я могу заставить это работать при настройке только имени класса? Объединить пространство имен с именем класса для полного имени типа? Есть еще один вызов рефлексии, который работает?

Если вы считаете, что это бесполезно, и я должен ожидать, что конфигурация будет содержать полное имя типа, я открыт для этого решения! Просто предоставьте обоснование, чтобы убедить меня.

(я не буду загружать тип извне этой сборки / пространства имен)

Ответы [ 5 ]

18 голосов
/ 17 ноября 2008

Либо используйте имя, указанное при сборке, либо возьмите сборку и используйте 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) имеет правила проверки, которые часто вызывают путаницу ... он просматривает вызывающую сборку и несколько системных сборок - но не все загруженные сборки.

6 голосов
/ 17 ноября 2008

Поскольку вы знаете, что все классы будут поступать из одного и того же пространства имен, настройте его один раз и используйте это:

<appConfig>
   <SuperAppConfig handlerNamespace="BigCorp.SuperApp">
      <Handler class="ClassB" />
   </SuperAppConfig>
</appConfig>

Редактировать: Я изменил имя на класс , чтобы лучше обозначить значение этого атрибута.

5 голосов
/ 17 ноября 2008
(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; 

Если вы ожидаете, что, возможно, сможете добавить дополнительные классы стратегии для загрузки в будущем, возможно, с помощью дополнительной сборки, вам необходимо будет полностью указать имя вашего класса. В любом случае это рекомендуется, поскольку вы сможете обеспечить расширенную расширяемость для своего приложения.

2 голосов
/ 18 ноября 2008

Я иду с полным именем типа в конфигурации приложения. Ниже приведен чуть более полный, но все же тривиальный пример

<SuperAppConfig>
   <ObjectConfig provider="BigCorp.SuperApp.ClassA">
      <add name="one" />
      <add name="two" />
   </ObjectConfig>
</SuperAppConfig>

И фабричный класс, который фактически создает это

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;
}
1 голос
/ 25 сентября 2011
BaseClass c = Activator.CreateInstance(t) as BaseClass; // fails

Возможно также, что CreateInstance не возвращает экземпляр BaseClass, а не экземпляр BaseClass, заключенный в ObjectHandle.

Приведение в ваш базовый класс после использования метода UnWrap.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...