实例化所有实现特定接口的类

20

我有一个接口IExample,以及一组类ClassOneClassTwoClassThree,它们都在不同的命名空间中定义。在开发的后期,我可能会删除其中任意一个类,或在新的位置添加一个新类。

现在,我想在运行时查找所有实现了IExample的类型并实例化它们。(我事先知道,实现IExample的任何类都不需要任何构造函数参数,但我不知道如何在代码中指定,所以是我而不是编译器知道...)

这种做法可行吗?我该如何去做?

更新:我现在尝试了几种建议的方法,但在所有方法中,我都得到了一个System.MissingMethodException异常,因为我“无法创建接口实例”。这是我的代码:

var tasks = AppDomain.CurrentDomain.GetAssemblies()
    .SelectMany(a => a.GetTypes())
    .Where(t => typeof(IBootstrapperTask).IsAssignableFrom(t))

    // This line is where it fails
    .Select(t => Activator.CreateInstance(t) as IBootstrapperTask)

    .ToArray();
new AutoMapperBootstrapper(tasks).Initialize();

如果没有as子句,我看不到任何异常,但是我得到了一个object[],而我需要一个IBootstrapperTask[]用于摘录中的最后一行的构造函数。我尝试了各种方法来进行转换,但是似乎都不起作用。

4个回答

34

这可以通过反射实现。例如:

var interfaceType = typeof(IExample);
var all = AppDomain.CurrentDomain.GetAssemblies()
  .SelectMany(x => x.GetTypes())
  .Where(x => interfaceType.IsAssignableFrom(x) && !x.IsInterface && !x.IsAbstract)
  .Select(x => Activator.CreateInstance(x));

注意:这将仅从当前AppDomain中加载的程序集创建IExample实例。这可能与当前进程中加载的所有程序集不同。但是对于许多类型的应用程序,这将是等效的。


4
更重要的是,它只包括已经被加载的程序集。可能还有其他已被引用但尚未加载的程序集。个人而言,我更喜欢保持所有内容清晰明确 :) - Jon Skeet

8

您需要知道一系列程序集来查找,但是使用LINQ可以相对容易地完成:

var instances = (from assembly in assemblies
                 from type in assembly
                 where !type.IsAbstract && 
                       type.IsClass &&
                       type.IsPublic &&
                       !type.IsGenericType &&
                       typeof(IExample).IsAssignableFrom(type)
                 let ctor = type.GetConstructor(Type.EmptyTypes)
                 where ctor != null && ctor.IsPublic
                 select (IExample) Activator.CreateInstance(type))
                .ToList();

你可能会想到其他要添加的限制,但它们很容易表达:)
然后,instances 将是一个 List<IExample>
编辑:我怀疑我的代码能够正常工作而你的不能,因为我明确地排除了非类。我猜测你的代码正在尝试实例化接口本身,即当 ttypeof(IBootstrapperTask) 时。

谢谢您的回复 - 请查看我的最新信息。 - Tomas Aschan
@Tomas:已编辑。你试过我的解决方案了吗?(如果需要的话,将最后一行更改为ToArray。)不可否认,我看不出as对你的原始代码有任何影响... - Jon Skeet
谢谢。当我仔细查看您的代码时,我注意到了公共、非抽象和带有构造函数的显式要求,并在我的代码中实现了它们。现在这个特定部分可以工作,但我还有其他(可能不相关)的问题,因此我无法确定它是否实际上可以工作,或者只是抛出了不同的异常。 - Tomas Aschan

5
你需要动态地实现这个吗?是否有时你可能不想创建一个?在我看来,这是依赖注入的一个好例子(可以进行配置)。例如,Unity有ResolveAll方法。
从上面的链接中;
IEnumerable<IMyObject> objects = myContainer.ResolveAll<IMyObject>();

仅凭我在问题中提供的有限示例信息,这是一个有效的论点,但在这种情况下,DI不适用。 - Tomas Aschan
哈 - 就因为我很快地放弃了 DI,结果最终还是用上了它... :P 谢谢!不过,我不会将其标记为答案,因为它并没有真正回答问题(“如何动态查找实现此接口的所有类?”),尽管它解决了我的实际问题。 - Tomas Aschan
+1 非常感谢!我一直在使用Caliburn.Micro构建项目来处理所有与MVVM相关的事情,但从未想过我也可以用它来解决这个问题!任何其他喜欢这个解决方案的人都应该尝试一下这个框架。 - CuddleBunny

0

尝试使用反射获取实现IExample的类型。这个例子查看当前程序集,但你可以轻松地传递不同的程序集:

        IEnumerable<Type> types = Assembly.GetExecutingAssembly()
            .GetTypes().Where(t=>t.GetInterfaces().Where(tt==typeof(IExample)).Count()>0);
        List<object> objects = new List<object>();
        foreach (Type type in types)
        {
            objects.Add(Activator.CreateInstance(type));
        }

为什么不直接调用 Contains 呢?而且,那样做无法处理继承。 - SLaks
1
这将失败,因为我无法创建接口的实例 - 我必须创建实现它们的类型的实例。 - Tomas Aschan
这不会失败!我正在对类型调用t.GetInterfaces()。我在我的一个项目中使用了类似的东西。 - Aliostad

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