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.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top