繁体   English   中英

如何注册和使用相同界面的不同实现?

[英]How to register and use different implementation of same interface?

假设我有这种情况:

using System.Linq;
using NUnit.Framework;
public interface IChannel {
   void Write(double value);
}

public class Channel: IChannel {
   private readonly int channelNumber;
   public Channel(int channelNumber) {
      Requires.That(channelNumber >= 0, "channelNumber >= 0");
      this.channelNumber = channelNumber;
   }
   private int calls;
   public void Write(double value) {
      System.Console.WriteLine("{0} wrote on channel {1} [#{2}]", value.ToString(), channelNumber, ++calls);
   }
}

public interface IService {
   void Do();
}

public class ServicePiGreek: IService {
   private readonly IChannel channel;
   public ServicePiGreek(IChannel channel) {
      Requires.IsNotNull(channel, "channel");
      this.channel = channel;
   }
   public void Do() {
      channel.Write(3.14);
   }
}
public class ServiceEuler: IService {
   private readonly IChannel channel;
   public ServiceEuler(IChannel channel) {
      Requires.IsNotNull(channel, "channel");
      this.channel = channel;
   }
   public void Do() {
      channel.Write(2.71);
   }
}

所以我会创建两个ServicePiGreek,通道0和1,以及一个ServiceEuler通道0:

[TestFixture]
public class Tests {
   [Test]public void without_simpleinjector() {
      var ch0 = new Channel(0);
      var s0 = new ServicePiGreek(ch0);
      var s1 = new ServicePiGreek(new Channel(1));
      var s2 = new ServiceEuler(ch0);
      s0.Do();
      s1.Do();
      s2.Do();
   }

我想到了这个:

[Test]public void with_simpleinjector() {
      SimpleInjector.Container container = new SimpleInjector.Container();
      container.RegisterAll(new Channel(0), new Channel(1));
      container.RegisterAll(GetAllServices(container));

      foreach (var service in container.GetAllInstances()) {
         service.Do();
      } 
   }

   private System.Collections.Generic.IEnumerable GetAllServices(SimpleInjector.Container container) {
      yield return new ServicePiGreek(container.GetAllInstances().ElementAt(1));
      yield return new ServicePiGreek(container.GetAllInstances().ElementAt(0));
      yield return new ServiceEuler(container.GetAllInstances().ElementAt(0));
   }

有没有人对如何做到这一点有更好的想法?

您的用例不是常用的,因为您在同一列表中具有多次相同的实现(作为瞬态),并且需要使用IChannel接口的不同实现来填充它。

我无法调查你的设计,但你现在如何注册这些类型是有道理的。 您注册了一个动态IEnumerable,它在迭代时回调容器。 很高兴看到您正在使用Simple Injector的一个较新功能,使用ElementAt (即O(1)操作)通过索引获取项目,因为返回的集合实现了IList<T>

您可以执行以下操作以使代码更具可读性:

private IEnumerable<IService> GetAllServices(Container container) {
  var channels = container.GetAllInstances<IChannel>();
  yield return new ServicePiGreek(channels.ElementAt(0));
  yield return new ServicePiGreek(channels.ElementAt(1));
  yield return new ServiceEuler(channels.ElementAt(0));

}

或者,当IService实现可以是单例时,您将能够执行以下操作:

var container = new SimpleInjector.Container();

var blueChannel = new Channel(0);
var redChannel = new Channel(1);

container.RegisterAll<IService>(
    new ServicePiGreek(blueChannel),
    new ServicePiGreek(redChannel),
    new ServiceEuler(blueChannel),  
);

foreach (var service in container.GetAllInstances<IService>()) {
    service.Do();
}

您可以在工厂后面抽象,而不是按索引请求元素,例如:

interface IChannelProvider
{
    IChannel GetBlueChannel();
    IChannel GetRedChannel();
}

但是,这取决于您的情况是否有效。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM