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

Jul*_*ech 3 .net c#

假设我有几个接口的实现:

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");
    }

}
Run Code Online (Sandbox Code Playgroud)

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

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

    foreach (var demoClass in demoClasses)
    {
        var implementation = demoClass as IDemoInterface;
        implementation.DemoMethod();
    }
}
Run Code Online (Sandbox Code Playgroud)

完成它的最佳方法是什么?使用Reflection是唯一的方法吗?

Sco*_*nen 5

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

var demoClasses = { DemoClass1, DemoClass2};
Run Code Online (Sandbox Code Playgroud)

...在您的问题中DemoClass,DemoClass2是类类型,而不是类型的实例.

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

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

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

换句话说,如果这些类中的一个具有如下构造函数:

public DemoClass1(string connectionString){
Run Code Online (Sandbox Code Playgroud)

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

如果您有一个实现该接口的接口和类,并且您想要获取这些类的实例,那么依赖注入容器(Ioc容器)可能会有所帮助.如果您还没有使用它,那么学习曲线就会有一点点,但它是一个非常有用的工具.这是一个示例(这是我的博客)将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")
            );
    }
}
Run Code Online (Sandbox Code Playgroud)

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

现在这个容器可以"解析"或返回一组实现IOrderValidator.请注意顶部附近的这条线:

container.Register(Component.For<OrderValidator>());
Run Code Online (Sandbox Code Playgroud)

这是班级:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

var validator = container.Resolve<OrderValidator>();
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

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