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

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

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

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

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

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

Я мог бы просто использовать контейнер IoC, работать с длинными именами и создавать объекты за меня!

Anthony Mastrean 01.12.2009 17:02
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
15
1
36 766
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

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

У меня есть ссылка на службу, которую я хочу вызвать с ее помощью, но она не будет отображаться в списке типов сборки (сделанной из класса). Хотя я могу создать объект ссылки, вызвав жесткий код конструктора.

MrFox 22.09.2015 17:00
(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.

Marc Gravell 17.11.2008 20:47
Ответ принят как подходящий

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

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

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

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

Anthony Mastrean 18.11.2008 00:43

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

<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.

Другие вопросы по теме