只知道类名如何创建一个对象?

15

我有一组类,每个类都是不同的策略,用于完成相同的工作。

namespace BigCorp.SuperApp
{
    public class BaseClass { }
    public class ClassA : BaseClass { }
    public class ClassB : BaseClass { }
}

可配置使用哪种策略。我希望在app.config文件中仅配置类名“ClassB”,而不是完整类型名称“BigCorp.SuperApp.ClassB”。

<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
5个回答

18

可以使用程序集限定名称(assembly-qualified-name),或获取程序集并使用 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

6

既然您知道所有类都来自同一个命名空间,请配置一次并重复使用:

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

编辑:我将name更改为class,以更好地表示该属性的含义。


我很感激有关程序集加载的答案、代码和讨论,但我喜欢Bryan的回答,因为他专注于配置(因为我无法避免完整类型名称)。 - Anthony Mastrean

5
(我不会从这个程序集/命名空间外加载类型)

由于上述行,可以安全地假设您知道命名空间是什么。你不能像这样做吗:

Type t = Type.GetType("Namespace." + className); 
BaseClass c = Activator.CreateInstance(t) as BaseClass; 

如果您希望未来可能能够添加额外的策略类(例如通过另一个程序集),则需要完全限定您的类名。无论如何,这都是推荐的做法,因为您将能够为应用程序提供增强的可扩展性。

这实际上取决于代码所在的位置(以及您如何解释“从外部” - 即是类还是调用者?)。如果没有程序集限定名称,Type.GetType(string)将仅查看当前程序集和一些系统程序集。它不会在随机引用的dll中找到类型。 - Marc Gravell

2

我会在应用程序配置中使用完整的类型名称。下面是一个稍微完整一些但仍然很简单的例子:

<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
BaseClass c = Activator.CreateInstance(t) as BaseClass; // fails

这可能是由于CreateInstance方法返回的不是BaseClass的实例,而是包装在ObjectHandle中的BaseClass实例。

在使用UnWrap方法后,将其转换为BaseClass。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接