How to register and use different implementation of same interface?

Suppose I have this scenario:

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

So I would create two ServicePiGreek with channel 0 and 1 and a ServiceEuler with channel 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();
   }

I thought of this:

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

Does anyone have any better ideas on how to accomplish this?


Your use case is not a usual one, since you have the same implementation multiple times (as transient) in the same list and need to fill it with different implementations of the IChannel interface.

I can't look into your design, but how you're registering the types right now makes sense. You register a dynamic IEnumerable that calls back upon the container upon iteration. It's nice to see that you're using one of the newer features of Simple Injector to get items by their index using ElementAt which is an O(1) operation, since the returned collection implements IList<T> .

You could do the following to make the code more readable:

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

}

Or when the IService implementations can be singleton, you would be able to do the following:

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

Instead of requesting elements by their index, you could abstract this behind a factory, for instance:

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

But, it depends on your scenario if this works.

链接地址: http://www.djcxy.com/p/49130.html

上一篇: 将方法类型参数约束为类泛型类型的基础

下一篇: 如何注册和使用相同接口的不同实现?