Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/257.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 依赖注入(DI)“;友好的;图书馆_C#_Dependency Injection_Inversion Of Control - Fatal编程技术网

C# 依赖注入(DI)“;友好的;图书馆

C# 依赖注入(DI)“;友好的;图书馆,c#,dependency-injection,inversion-of-control,C#,Dependency Injection,Inversion Of Control,我正在考虑一个C#库的设计,它将有几个不同的高级功能。当然,这些高级函数将尽可能使用类设计原则来实现。因此,可能会有供消费者定期直接使用的类,以及作为那些更常见的“最终用户”类的依赖项的“支持类” 问题是,设计图书馆的最佳方式是什么: DI不可知-虽然为一个或两个常用DI库(StructureMap、Ninject等)添加基本“支持”似乎是合理的,但我希望消费者能够将该库与任何DI框架一起使用 不可使用DI—如果库的使用者不使用DI,则库仍应尽可能易于使用,从而减少用户创建所有这些“不重要”依

我正在考虑一个C#库的设计,它将有几个不同的高级功能。当然,这些高级函数将尽可能使用类设计原则来实现。因此,可能会有供消费者定期直接使用的类,以及作为那些更常见的“最终用户”类的依赖项的“支持类”

问题是,设计图书馆的最佳方式是什么:

  • DI不可知-虽然为一个或两个常用DI库(StructureMap、Ninject等)添加基本“支持”似乎是合理的,但我希望消费者能够将该库与任何DI框架一起使用
  • 不可使用DI—如果库的使用者不使用DI,则库仍应尽可能易于使用,从而减少用户创建所有这些“不重要”依赖项所需的工作量,以获得他们想要使用的“真实”类
我目前的想法是为公共DI库(例如StructureMap注册表、Ninject模块)提供一些“DI注册模块”,以及一个非DI的集合或工厂类,它们包含与这些工厂的耦合


想法?

我要做的是以DI容器无关的方式设计我的库,以尽可能地限制对容器的依赖。如果需要的话,这允许在DI容器上交换另一个容器

然后将DI逻辑上面的层公开给库的用户,以便他们可以使用您通过接口选择的任何框架。通过这种方式,他们仍然可以使用您公开的DI功能,并且他们可以出于自己的目的自由地使用任何其他框架


允许库的用户插入他们自己的DI框架对我来说似乎有点错误,因为它极大地增加了维护量。与直接DI相比,这更像是一个插件环境。

编辑2015:时间过去了,我现在意识到这一切都是一个巨大的错误。IoC容器非常糟糕,DI是处理副作用的非常糟糕的方法。实际上,这里的所有答案(以及问题本身)都是要避免的。只需注意副作用,将其与纯代码分开,其他一切要么就到位了,要么就是不相关和不必要的复杂性

原答覆如下:


在开发过程中,我不得不面对同样的决定。我一开始的目标是DI友好和容器无关,但随着我添加越来越多的内部组件,内部工厂很快变得不可管理,产生的库也变得不灵活

最后我自己写了一本,同时还提供了一个和一个。将库与其他容器集成只是正确连接组件的问题,因此我可以轻松地将其与Autofac、Unity、StructureMap等集成

这样做的缺点是,我失去了对服务进行
new
升级的能力。我还采用了一种我本可以避免的依赖性(我将来可能会对其进行重构),以使嵌入式容器更易于实现

更多详细信息,请参阅本文

似乎依赖于类似的东西。它有一个接口,它实际上是CommonServiceLocator的IServiceLocator,还有几个方法,然后它为每个容器实现这个接口,即,和一个常规模块/设施,即。然后,它需要实例化它需要的东西。当然,这是一种有效的方法,但我个人不太喜欢它,因为它实际上在容器中传递了太多信息,将其用作服务定位器

也实现了,这实现了好旧。此容器通过在整个框架中使用。为了得到混凝土容器,它有一个。将此服务提供商定位器指向温莎,使其成为选定的服务提供商

一句话:没有完美的解决方案。与任何设计决策一样,这个问题需要在灵活性、可维护性和方便性之间取得平衡。

术语“依赖注入”与IoC容器没有任何具体关系,即使您倾向于将它们一起提到。这仅仅意味着,不要像这样编写代码:

public class Service
{
    public Service()
    {
    }

    public void DoSomething()
    {
        SqlConnection connection = new SqlConnection("some connection string");
        WindowsIdentity identity = WindowsIdentity.GetCurrent();
        // Do something with connection and identity variables
    }
}
public class Service
{
    public Service(IDbConnection connection, IIdentity identity)
    {
        this.Connection = connection;
        this.Identity = identity;
    }

    public void DoSomething()
    {
        // Do something with Connection and Identity properties
    }

    protected IDbConnection Connection { get; private set; }
    protected IIdentity Identity { get; private set; }
}
public class MyFacade
{
    private IMyDependency dep;

    public MyFacade()
    {
        this.dep = new DefaultDependency();
    }

    public MyFacade WithDependency(IMyDependency dependency)
    {
        this.dep = dependency;
        return this;
    }

    public Foo CreateFoo()
    {
        return new Foo(this.dep);
    }
}
你是这样写的:

public class Service
{
    public Service()
    {
    }

    public void DoSomething()
    {
        SqlConnection connection = new SqlConnection("some connection string");
        WindowsIdentity identity = WindowsIdentity.GetCurrent();
        // Do something with connection and identity variables
    }
}
public class Service
{
    public Service(IDbConnection connection, IIdentity identity)
    {
        this.Connection = connection;
        this.Identity = identity;
    }

    public void DoSomething()
    {
        // Do something with Connection and Identity properties
    }

    protected IDbConnection Connection { get; private set; }
    protected IIdentity Identity { get; private set; }
}
public class MyFacade
{
    private IMyDependency dep;

    public MyFacade()
    {
        this.dep = new DefaultDependency();
    }

    public MyFacade WithDependency(IMyDependency dependency)
    {
        this.dep = dependency;
        return this;
    }

    public Foo CreateFoo()
    {
        return new Foo(this.dep);
    }
}
也就是说,在编写代码时要做两件事:

  • 当您认为实现可能需要更改时,依赖接口而不是类

  • 不要在类中创建这些接口的实例,而是将它们作为构造函数参数传递(或者,可以将它们分配给公共属性;前者是构造函数注入,后者是属性注入)

  • 所有这些都不是以任何DI库的存在为前提的,没有DI库也不会使代码编写变得更加困难

    如果您正在寻找这方面的示例,请只查看.NET Framework本身:

    • List
      实现
      IList
      。如果将类设计为使用
      IList
      (或
      IEnumerable
      ),则可以利用延迟加载等概念,如Linq到SQL、Linq到实体和NHibernate,所有这些都是通过属性注入在幕后完成的。一些框架类实际上接受
      IList
      作为构造函数参数,例如
      BindingList
      ,它用于几个数据绑定特性

    • Linq到SQL和EF完全围绕
      IDbConnection
      和相关接口构建,这些接口可以通过公共构造函数传入。不过,你不需要使用它们;默认构造函数在配置文件中的某个位置使用连接字符串时工作正常

    • 如果您使用WinForms组件,您将处理“服务”,例如
      InamCreationService
      IExtenderProviderService
      。你甚至都不知道