迭代接口的实现并调用方法

时间:2018-02-10 00:34:21

标签: c# .net

假设我有几个接口实现:

public interface IDemoInterface
{
    void DemoMethod();
}

public class DemoClass1 : IDemoInterface
{
    public void DemoMethod()
    {
        Console.WriteLine("DemoClass1");
    }

}

public class DemoClass2 : IDemoInterface
{
    public void DemoMethod()
    {
        Console.WriteLine("DemoClass2");
    }

}

现在,我正在尝试定义此类实现的数组,并为每个实现调用接口方法。基本上,做这样的事情(以下是一个非工作代码 - 只是我想要做的事情的例证):

public static void Run()
{
    var demoClasses = { DemoClass1, DemoClass2};

    foreach (var demoClass in demoClasses)
    {
        var implementation = demoClass as IDemoInterface;
        implementation.DemoMethod();
    }
}

实现它的最佳方法是什么?是否只使用Reflection?

3 个答案:

答案 0 :(得分:5)

从您的示例中,您似乎并未尝试遍历实现接口的类的实例。你试图循环遍历类型本身。我是基于这个......

var demoClasses = { DemoClass1, DemoClass2};

...在您的问题中DemoClassDemoClass2是类类型,而不是类型的实例。

您可以从一组类型开始,然后创建每种类型的实例,如下所示。 (您也可以使用反射来查找实现您的界面的类类型。)

var types = new Type[] {typeof(DemoClass1), typeof(DemoClass1)};
foreach (var type in types)
{
    if (typeof(IDemoInterface).IsAssignableFrom(type))
    {
        var instance = Activator.CreateInstance(type) as IDemoInterface;
        instance.DemoMethod();
    }
}

这会有效,但是有一个很大的限制:每个类都必须有一个默认的构造函数。或者如果你要打电话给构造函数,他们都需要有相同的构造函数。

换句话说,如果其中一个类有这样的构造函数:

public DemoClass1(string connectionString){

然后Activator.CreateInstance会爆炸,除非你给它传递给构造函数的值。但是如果你有一个类型的数组,并且它们有不同的构造函数,那么这几乎是不可能的。而且你不想把自己画成一个角落,你必须编写只能有空构造函数的类。

当你有一个实现这些接口的接口和类,并且你想获得这些类的实例时,依赖注入容器(Ioc容器)可能会有所帮助。如果您还没有使用它,那么学习曲线就会有一点点,但它是一个非常有用的工具。以下是将my blog容器配置为“register”的示例(这是Windsor),或者声明接口的多个实现。

(如果你不熟悉它,它似乎完全不合适,这就是为什么我也链接到Windsor的文档。)

public class DependencyRegistration : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        container.Kernel.Resolver.AddSubResolver(new CollectionResolver(container.Kernel));
        container.Register(Component.For<OrderValidator>());

        container.Register(
            Component.For<IOrderValidator,AddressValidator>()
                .Named("AddressValidator"),
            Component.For<IOrderValidator,OrderLinesValidator>()
                .Named("OrderLinesValidator"),
            Component.For<IOrderValidator,ProductStateRestrictionsValidator>()
                .Named("ProductStateRestrictionsValidator")
            );
    }
}

在这个例子中,我告诉这个“容器”,有三个类实现IOrderValidator

现在,这个容器可以“解析”或返回IOrderValidator的一系列实现。请注意顶部附近的这条线:

container.Register(Component.For<OrderValidator>());

这是班级:

public class OrderValidator : IOrderValidator
{
    private readonly IOrderValidator[] _subValidators;

    public OrderValidator(IOrderValidator[] subValidators)
    {
        _subValidators = subValidators;
    }

    public IEnumerable<ValidationMessage> GetValidationMessages(Order order)
    {
        var validationMessages = new List<ValidationMessage>();
        foreach(var validator in _subValidators)
        {
            validationMessages.AddRange(validator.GetValidationMessages(order));
        }
        return validationMessages;
    }
}

请注意,在构造函数中有一个IOrderValidator数组。如果我打电话

var validator = container.Resolve<OrderValidator>();

容器会创建OrderValidator的实例。它会检测到构造函数需要一个IOrderValidator数组,因此它会创建所有其他类的实例并将它们放在数组中。

如果这些类中的任何一个也有需要其他值或类的构造函数,并且我告诉容器如何创建它们,那么它将根据需要创建它们以便能够创建IOrderValidator的实现。

结果是我可以有很多类的实现,每个类都有不同的构造函数依赖项,我可以创建这些实现的集合。

这个答案并没有真正告诉你如何做到这一点,但希望它能说明在哪里可以找到完成这类工作的工具。正确使用DI容器是非常有用的工具。这在大型应用程序甚至是小型应用程序中都很常见,因为它可以更轻松地管理许多类型,这些类型可以互相嵌套,同时保持理智和可理解。它还有助于我们编写更小,更容易进行单元测试的类。

除了Windsor之外,其他一些容器还包括Autofac,Unity,SimpleInjector以及ASP.NET核心应用程序中包含的DI容器。

答案 1 :(得分:3)

您的代码大部分都是正确的:

private static void Main()
{
    var demoClasses = new List<IDemoInterface> {new DemoClass1(), new DemoClass2()};

    foreach (var demoClass in demoClasses)
    {
        demoClass.DemoMethod();
    }

    Console.Write("\nDone!\nPress any key to exit...");
    Console.ReadKey();
}

<强>输出

enter image description here

答案 2 :(得分:2)

从问题来看,我不清楚您是希望自己初始化类型,还是想要像Rufus's回答那样做。

但是,如果您事先不知道实现,理论上可以扫描它们,并检索所有类型

// can throw a ReflectionTypeLoadException in case not all dlls are known
private static IEnumerable<Type> GetImplementationsOf<TInterface>() {
  var interfaceType = typeof( TInterface );
  return AppDomain.CurrentDomain.GetAssemblies()
    .Select( assembly => assembly.GetTypes().Where( type => !type.IsInterface && interfaceType.IsAssignableFrom( type ) ) )
    .SelectMany( implementation => implementation );
}

此代码将返回与通用类型参数匹配的IEnumerable<Type>,并且它不是一个接口(虽然理论上,这部分可以让您的消费者处理)

之后,您可以浏览类型列表并创建它们的实例并运行您的代码,如下所示:

var types = GetImplementationsOf<IDemoInterface>();
foreach (var type in types) {
  // will throw an exception in case there is no parameterless constructor
  var impl = (IDemoInterface) Activator.CreateInstance( type );
  impl.DemoMethod();
}