C# 具有两个实现相同接口的类的IOC/DI

C# 具有两个实现相同接口的类的IOC/DI,c#,.net,design-patterns,dependency-injection,inversion-of-control,C#,.net,Design Patterns,Dependency Injection,Inversion Of Control,我对两个类实现相同接口和依赖注入的场景感到困惑 public interface ISomething { void DoSomething(); } public class SomethingA : ISomething { public void DoSomething() { } } public class SomethingAB : ISomething { public void DoSomething() { } } public class

我对两个类实现相同接口和依赖注入的场景感到困惑

public interface ISomething
{
  void DoSomething();
}

public class SomethingA : ISomething
{
  public void DoSomething()
  {

  }
}

public class SomethingAB : ISomething
{
  public void DoSomething()
  {

  }
}

public class Different
{
  private ISomething ThisSomething;

  public Different(ISomething Something)
  {  
    ThisSomething = Something;
  }
}
我在网上看到过一些例子,说这是有效的,但是你一次只能使用一个类。因此,如果应用程序在SiteA上运行,你会告诉你的IOC使用某些东西A,但如果应用程序在SiteB上运行,你会告诉它使用某些东西B

因此,让一个应用程序有两个类来实现一个接口并尝试同时使用这两个类,这被认为是不好的做法吗?如果不是,你如何告诉国际奥委会在相关情况下使用哪个级别

更新:为了更好地解释它,我将使用Ninject的示例:

public class Samurai 
{
    private IWeapon Weapon;

    public Samurai(IWeapon weapon) 
    {
        this.Weapon = weapon;
    }
}

public class Sword : IWeapon
{
...
}

public class Gun : IWeapon
{
...
}

public class WarriorModule : NinjectModule
{
    public override void Load() 
    {
        this.Bind<IWeapon>().To<Sword>();
        this.Bind<IWeapon>().To<Gun>();  //Just an example
    }
}
公共级武士
{
私人武器;
公共武士(IWeapon武器)
{
这个。武器=武器;
}
}
公共级剑:IWeapon
{
...
}
公共级枪:IWeapon
{
...
}
公共类WarriorModule:NinjectModule
{
公共覆盖无效负载()
{
this.Bind().To();
this.Bind().To();//只是一个例子
}
}

现在有两个类使用IWeapon。根据应用程序中的某些内容或上下文,您希望武士有时拥有一把剑,或者在其他地方拥有一把枪。你是如何做到这一点的?你如何处理这种“如果”的情况???

在这种情况下,我与Unity和spring合作,我认为兴趣在于在包(即类)之间具有弱耦合,改变服务或入口点的能力是国际奥委会的结果

ioc提供服务使用的灵活性,或从服务实现相同接口时起

如果使用服务A,服务B和服务在服务包A中,包B在B中。 包A在包b上没有引用,但服务A在包含接口的包上有引用。
因此,我们得出结论,在包a和包b之间存在弱耦合。

我不认为这在一般情况下是一种不好的做法。在某些情况下,您可能需要在同一应用程序中使用同一接口的不同实现,并根据上下文使用一个或另一个实现

至于如何配置您的DI以启用此场景,当然,这将取决于您的DI:-)一些可能不支持它,其他可能不支持,其他可能部分支持它,等等

例如,您可以使用以下类:

public interface ISomething
{
}

public class SomethingA : ISomething
{
}

public class SomethingB : ISomething
{
}

public class Foo
{
    public Foo(ISomething something)
    {
        Console.WriteLine(something);
    }
}

public class Bar
{
    public Bar(ISomething something)
    {
        Console.WriteLine(something);
    }
}
然后在配置内核时使用命名绑定:

// We create the kernel that will be used to provide instances when required
var kernel = new StandardKernel();

// Declare 2 named implementations of the same interface
kernel.Bind<ISomething>().To<SomethingA>().Named("somethingA");
kernel.Bind<ISomething>().To<SomethingB>().Named("somethingB");

// inject SomethingA into Foo's constructor
kernel.Bind<Foo>().ToSelf().WithConstructorArgument(
    "something", ctx => ctx.Kernel.Get<ISomething>("somethingA")
);

// inject SomethingB into Bar's constructor
kernel.Bind<Bar>().ToSelf().WithConstructorArgument(
    "something", ctx => ctx.Kernel.Get<ISomething>("somethingB")
);

将多个实现映射到同一个接口并不是很糟糕的做法,但这不是最常见的使用模式


您没有指定特定的DI工具,但如果使用Unity,则可以对命名实例执行此操作。请参阅:

个人不会认为这是一个糟糕的实践-它是一个主要的点定义接口首先!至于如何让你的IOC容器支持这一点,这在很大程度上取决于IOC,我使用的所有容器都支持各种条件配置选项。添加一个具体的国际奥委会来得到答案。问题的存在并不是具体的国际奥委会,而是我的理解。我假设自己实例化类不是最佳实践,如果可以的话,我假设我需要使我的IOC在全球可见,以获得正确的实例,我也不确定这是否是最佳实践。IOC的一般“最佳实践”是只在一个地方拥有它。也有例外,但这是经验法则。不过,其中一个例外情况是,您需要这种运行时配置—假设您有一个事件,该事件创建了一个需要可配置依赖项的类,您可以在该事件的处理程序(IMO)中引用您的IOC。一个全局可见的IOC是一个大的反模式(服务定位器)。关于一个具体的例子,请参见Darin的回答:)是否创建了两个类来接受不必要的重复代码?所有这些都是在程序开始时完成的,但这可能只是一个学习曲线,但我假设我将不再在代码中实例化类??我不明白为什么这会是重复的代码。您可能有两个类需要相同的契约(ISomething),这些类的使用者根据上下文决定需要注入不同的实现。此外,我不会在应用程序中请求Foo或Bar的实例,因为我需要使内核全局可见??不,您永远不会这样做。内核应该是绝对透明的。您不应该在消费代码中直接使用它。例如,在ASP.NET MVC中,您可以使用带有NInject(NInject.MVC3 NuGet)的自定义DependencyResolver,它将直接将您指示的依赖项注入控制器。在99%的应用程序中,您从不编写诸如
kernel.Get()
之类的代码。您永远不需要访问内核。您可以在应用程序的生命周期内配置一次,然后让它完成它的工作。谢谢。我不知道在现阶段,这种情况可能是什么,我只是想学习,但我意识到在某些时候,会/必须有两个类实现同一个接口的场景,所以必须有一种方法告诉程序使用一个或另一个基于某个东西的类…不管是什么。接口的多个实现实际上就是为什么在第一名。让我们不要用哑DI框架的pupets来考虑接口。OP问“这被认为是不好的做法吗”,我回答“将多个实现映射到同一个接口并不是真正的坏做法”。。。我们同意,而你却对我投了反对票?你说“这不是最常见的使用模式”,而事实上,这是非常普遍的。为类编写接口是为了编写接口而不是在其他地方重用它吗?
var foo = kernel.Get<Foo>();
var bar = kernel.Get<Bar>();