У меня есть набор классов, каждый из которых является другим стратегия для выполнения той же работы.
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
Как я могу заставить это работать при настройке только имени класса? Объединить пространство имен с именем класса для полного имени типа? Есть ли еще один работающий вызов отражения?
Если вы думаете, что это бесполезно, и я должен ожидать, что конфигурация будет содержать полное имя типа, я открыт для этого решения! Просто дайте обоснование, чтобы убедить меня.
(Я не буду загружать тип за пределами этой сборки / пространства имен)





Либо используйте имя с указанием сборки, либо получите сборку и используйте 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) имеет правила проверки, которые часто вызывают путаницу ... он смотрит на вызывающую сборку и несколько системных сборок, но не на все загруженные сборки.
У меня есть ссылка на службу, которую я хочу вызвать с ее помощью, но она не будет отображаться в списке типов сборки (сделанной из класса). Хотя я могу создать объект ссылки, вызвав жесткий код конструктора.
(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;
Если вы ожидаете, что, возможно, сможете добавить дополнительные классы стратегии для загрузки в будущем, возможно, через дополнительную сборку, вам нужно будет полностью указать имя вашего класса. В любом случае это рекомендуется, поскольку вы сможете обеспечить расширяемость вашего приложения.
На самом деле это зависит от того, где находится этот код (и как вы интерпретируете «извне» - то есть это класс? Или вызывающий?). Без имени с указанием сборки Type.GetType (string) будет Только просматривать текущую сборку и несколько системных сборок. Он не найдет типы в случайных ссылках на DLL.
Поскольку вы знаете, что все классы будут происходить из одного и того же пространства имен, настройте его один раз и используйте:
<appConfig>
<SuperAppConfig handlerNamespace = "BigCorp.SuperApp">
<Handler class = "ClassB" />
</SuperAppConfig>
</appConfig>
Редактировать: Я изменил имя на класс, чтобы лучше обозначить значение этого атрибута.
Я ценю ответы, код и обсуждение загрузки сборки. Но мне нравится ответ Брайана за то, что он сосредоточен на конфигурации (поскольку я не могу уйти от полного имени типа).
Я использую полное имя типа в конфигурации приложения. Ниже представлен чуть более полный, но все же тривиальный пример.
<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;
}
BaseClass c = Activator.CreateInstance(t) as BaseClass; // fails
Это также может быть результатом того факта, что CreateInstance не возвращает экземпляр BaseClass, а не экземпляр BaseClass, заключенный в ObjectHandle.
Добавьте в свой BaseClass после того, как вы использовали метод UnWrap.
Я мог бы просто использовать контейнер IoC, работать с длинными именами и создавать объекты за меня!