C# 依赖注入(DI)“;友好的;图书馆
我正在考虑一个C#库的设计,它将有几个不同的高级功能。当然,这些高级函数将尽可能使用类设计原则来实现。因此,可能会有供消费者定期直接使用的类,以及作为那些更常见的“最终用户”类的依赖项的“支持类” 问题是,设计图书馆的最佳方式是什么:C# 依赖注入(DI)“;友好的;图书馆,c#,dependency-injection,inversion-of-control,C#,Dependency Injection,Inversion Of Control,我正在考虑一个C#库的设计,它将有几个不同的高级功能。当然,这些高级函数将尽可能使用类设计原则来实现。因此,可能会有供消费者定期直接使用的类,以及作为那些更常见的“最终用户”类的依赖项的“支持类” 问题是,设计图书馆的最佳方式是什么: DI不可知-虽然为一个或两个常用DI库(StructureMap、Ninject等)添加基本“支持”似乎是合理的,但我希望消费者能够将该库与任何DI框架一起使用 不可使用DI—如果库的使用者不使用DI,则库仍应尽可能易于使用,从而减少用户创建所有这些“不重要”依
- DI不可知-虽然为一个或两个常用DI库(StructureMap、Ninject等)添加基本“支持”似乎是合理的,但我希望消费者能够将该库与任何DI框架一起使用
- 不可使用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);
}
}
也就是说,在编写代码时要做两件事:
实现List
。如果将类设计为使用IList
(或IList
),则可以利用延迟加载等概念,如Linq到SQL、Linq到实体和NHibernate,所有这些都是通过属性注入在幕后完成的。一些框架类实际上接受IEnumerable
作为构造函数参数,例如IList
,它用于几个数据绑定特性BindingList
- Linq到SQL和EF完全围绕
和相关接口构建,这些接口可以通过公共构造函数传入。不过,你不需要使用它们;默认构造函数在配置文件中的某个位置使用连接字符串时工作正常IDbConnection
- 如果您使用WinForms组件,您将处理“服务”,例如
或InamCreationService
。你甚至都不知道IExtenderProviderService