主要的C# DI/IoC框架有哪些,它们之间有什么区别?

319

冒着涉足圣战领域的风险,这些热门DI/IoC框架的优缺点是什么?是否可以轻易地被认为是最好的?..:

  • Ninject
  • Unity
  • Castle.Windsor
  • Autofac
  • StructureMap

还有其他未在此处列出的用于C#的DI/IoC框架吗?

在我的使用情况下,我正在构建一个客户端WPF应用程序和一个WCF/SQL服务基础设施,在选择中易用性(特别是在清晰简洁的语法方面)、一致的文档、良好的社区支持和性能都是重要因素。

更新:

引用的资源和重复问题似乎已过时,是否可以有知道所有这些框架的人提供一些真实的见解?

我意识到这个主题上的大多数意见可能是有偏见的,但我希望有人花时间研究所有这些框架,并至少有一个普遍客观的比较。

如果以前没有进行过这样的研究,我很愿意进行自己的调查,但我认为这是一些人已经做过的事情。

第二次更新:

如果您具有使用多个DI/IoC容器的经验,请排名并总结这些容器的优缺点,谢谢。这不是发现所有人都制作的鲜为人知的小容器的练习,我正在寻找流行(且活跃)框架之间的比较。


1
Ninject vs Unity for DI相同的问题,但现在可能需要跟进。 - Matthew Flaschen
2
可能是重复的问题:比较Castle Windsor,Unity和StructureMap - Mauricio Scheffer
@chibacity - 我在4个项目中使用过Unity,前两个非常基础,没有问题,后两个由于构造函数注入、可维护性和可读性方面的问题,Unity给我们带来了很多麻烦。最终我们将Unity从这两个项目中移除,并用StructureMap替换它,构造函数注入非常简单,配置干净且易于维护。在我的个人时间里,我尝试过AutoFac,但我发现它有些棘手,需要一些文档才能更好地理解它。至于其他的,我只能根据我所读到的进行评论。 - Phill
因此,与“ObjectFactory.AssertConfigurationIsValid()”一起使用,使得StructureMap更容易调试。StructureMap允许以多种方式进行配置,对于Unity,我们在代码中进行了配置,并尝试了XML,这两种方式都需要60多行XML或代码来进行配置。使用注册表,属性和XML,我们将配置减少到约10行,并且自那以后就没有再次修改过它,这使得其他从未使用过IoC容器的开发人员更容易理解StructureMap。因此,能够以多种方式配置StructureMap非常方便。 - Phill
它真的吗?DI/IoC是一个相当狭窄的领域,肯定可以做得好或不好。这不像比较IDE或语言等广泛的技术。尽管某些实现可能达到难以区分优越性的程度,并且它们展示了卓越和完整的水平,但了解哪些是这些实现,以及特定方面是否将其推向前沿,是非常值得的。这不是Emacs vs Vim。 - ocodo
显示剩余5条评论
7个回答

235

虽然对这个问题的全面回答需要数百页的我的书,但以下是我正在制作的一个快速比较表:

一张表格,解释了几种DIC之间的区别


42
我已经阅读了你书的 MEAP ,一直在想为什么你没有提到 Ninject?请问原因是什么? - Martin Owen
25
@Mark,谢谢你的回答,希望你的答案可以包括Ninject(很重要,不仅因为它最近很火,而且还因为它使用了新的语言特性)。 - ocodo
3
尽管Ninject在很多方面与AutoFac相似,但它被NUGET团队使用,并且是最受欢迎的IOC容器之一。我很失望Mark的《.NET依赖注入》书中没有涉及它。如果有第二版,希望可以加入。我经常使用Unity、MEF(不是真正的依赖注入)、Ninject或StructurMap,但还没有遇到过使用Spring.net或AutoFac等的合同或远程工作机会。 - Tom Stickel
2
Unity 3.5已经支持基于约定的注册:https://www.nuget.org/packages/Unity/3.5.1404。移除了一个缺点;-) - Vladimir Dorokhov
1
@OhadSchneider 我仍然推荐使用Pure DI,除非你有非常具体的要求。 - Mark Seemann
显示剩余12条评论

116

我发现了另一个性能 比较(最新更新于2014年4月10日)。它比较了以下内容:

这里是帖子的简要概述:

结论 Ninject绝对是最慢的容器。 MEF、LinFu和Spring.NET比Ninject快,但仍然很慢。AutoFac、Catel和Windsor排名第二,接下来是StructureMap、Unity和LightCore。Spring.NET的一个缺点是只能用XML配置。 SimpleInjector、Hiro、Funq、Munq和Dynamo提供了最佳性能,它们非常快。试试它们吧! 特别是Simple Injector似乎是一个不错的选择。它非常快,有良好的文档,并且还支持像拦截和通用装饰器这样的高级场景。
您还可以尝试使用Common Service Selector Library,并希望尝试多个选项,看看哪个最适合您。
关于Common Service Selector Library的一些信息来自该网站:
该库提供了对IoC容器和服务定位器的抽象。使用该库允许应用程序间接访问功能,而不依赖于硬引用。希望使用此库,第三方应用程序和框架可以开始利用IoC/服务定位,而不将自己束缚于特定实现。
更新 2011年9月13日:FunqMunq被添加到参赛者列表中。图表也进行了更新,由于其性能较差,Spring.NET已被删除。

04.11.2011: "添加了Simple Injector,性能是所有竞争者中最好的"。


最近更新,看到速度差异很有趣(加上基本功能矩阵)。谢谢。 - lko
那个比较并不是很可靠,因为据我所知,Ninject具有拦截和XML配置的扩展,而比较表明它没有。 - Daniel B
15
这是一个非常数量化的比较。那么像文件大小或所需依赖项数量这样的非性能特征呢?此外,主观的度量标准,如文档质量或可用性会有所帮助。我的观点是,除了速度之外还有其他要考虑的因素。 - FistOfFury
1
就像 StructureMap 的作者 Jeremy Miller 曾经说过的那样... 重新表述一下 -- 确实有更快的 IOC 容器,但它们缺乏完整的功能集。 - Tom Stickel
请查看此链接:http://www.iocservicestack.net - Rajesh Jinaga

49

5
关于 .Net DI Container Speed Redux 的帖子有一个更新:在底线部分,Unity 最初采取了错误的方法。通过新的测量数据,Unity 看起来好多了。 - Volker von Einem

35

免责声明:截至2015年初,Jimmy Bogard对IoC容器的功能进行了很好的比较,以下是摘要:

比较的容器:

  • Autofac
  • Ninject
  • Simple Injector
  • StructureMap
  • Unity
  • Windsor

场景是这样的:我有一个接口IMediator,在其中可以向多个接收者发送单个请求/响应或通知:

public interface IMediator 
{ 
    TResponse Send<TResponse>(IRequest<TResponse> request);

    Task<TResponse> SendAsync<TResponse>(IAsyncRequest<TResponse> request);

    void Publish<TNotification>(TNotification notification)
        where TNotification : INotification;

    Task PublishAsync<TNotification>(TNotification notification)
        where TNotification : IAsyncNotification; 
}

我随后创建了一组基本的请求/响应/通知:

public class Ping : IRequest<Pong>
{
    public string Message { get; set; }
}
public class Pong
{
    public string Message { get; set; }
}
public class PingAsync : IAsyncRequest<Pong>
{
    public string Message { get; set; }
}
public class Pinged : INotification { }
public class PingedAsync : IAsyncNotification { }

我对容器支持泛型的几个方面感兴趣:

  • 开放式泛型的设置(轻松注册IRequestHandler<>)
  • 多个开放式泛型的注册设置(两个或更多INotificationHandlers)

泛型差异的设置(为基础INotification注册处理程序/创建请求管道)。 我的处理程序非常简单,只是输出到控制台:

public class PingHandler : IRequestHandler<Ping, Pong> { /* Impl */ }
public class PingAsyncHandler : IAsyncRequestHandler<PingAsync, Pong> { /* Impl */ }

public class PingedHandler : INotificationHandler<Pinged> { /* Impl */ }
public class PingedAlsoHandler : INotificationHandler<Pinged> { /* Impl */ }
public class GenericHandler : INotificationHandler<INotification> { /* Impl */ }

public class PingedAsyncHandler : IAsyncNotificationHandler<PingedAsync> { /* Impl */ }
public class PingedAlsoAsyncHandler : IAsyncNotificationHandler<PingedAsync> { /* Impl */ }

Autofac

var builder = new ContainerBuilder();
builder.RegisterSource(new ContravariantRegistrationSource());
builder.RegisterAssemblyTypes(typeof (IMediator).Assembly).AsImplementedInterfaces();
builder.RegisterAssemblyTypes(typeof (Ping).Assembly).AsImplementedInterfaces();
  • 开放泛型:是,隐式
  • 多重开放泛型:是,隐式
  • 泛型逆变:是,显式

Ninject

var kernel = new StandardKernel();
kernel.Components.Add<IBindingResolver, ContravariantBindingResolver>();
kernel.Bind(scan => scan.FromAssemblyContaining<IMediator>()
    .SelectAllClasses()
    .BindDefaultInterface());
kernel.Bind(scan => scan.FromAssemblyContaining<Ping>()
    .SelectAllClasses()
    .BindAllInterfaces());
kernel.Bind<TextWriter>().ToConstant(Console.Out);
  • 开放式泛型:是的,隐式
  • 多个开放式泛型:是的,隐式
  • 泛型逆变性:是的,使用用户构建的扩展

Simple Injector

var container = new Container();
var assemblies = GetAssemblies().ToArray();
container.Register<IMediator, Mediator>();
container.Register(typeof(IRequestHandler<,>), assemblies);
container.Register(typeof(IAsyncRequestHandler<,>), assemblies);
container.RegisterCollection(typeof(INotificationHandler<>), assemblies);
container.RegisterCollection(typeof(IAsyncNotificationHandler<>), assemblies);
  • 开放式泛型: 是的,显式声明
  • 多个开放式泛型: 是的,显式声明
  • 泛型逆变性: 是的,隐式声明(从3.0版本开始)

StructureMap

var container = new Container(cfg =>
{
    cfg.Scan(scanner =>
    {
        scanner.AssemblyContainingType<Ping>();
        scanner.AssemblyContainingType<IMediator>();
        scanner.WithDefaultConventions();
        scanner.AddAllTypesOf(typeof(IRequestHandler<,>));
        scanner.AddAllTypesOf(typeof(IAsyncRequestHandler<,>));
        scanner.AddAllTypesOf(typeof(INotificationHandler<>));
        scanner.AddAllTypesOf(typeof(IAsyncNotificationHandler<>));
    });
});
  • 开放式泛型: 是的, 显式声明
  • 多重开放式泛型: 是的, 显式声明
  • 泛型逆变性: 是的, 隐式声明

Unity

container.RegisterTypes(AllClasses.FromAssemblies(typeof(Ping).Assembly),
   WithMappings.FromAllInterfaces,
   GetName,
   GetLifetimeManager);

/* later down */

static bool IsNotificationHandler(Type type)
{
    return type.GetInterfaces().Any(x => x.IsGenericType && (x.GetGenericTypeDefinition() == typeof(INotificationHandler<>) || x.GetGenericTypeDefinition() == typeof(IAsyncNotificationHandler<>)));
}

static LifetimeManager GetLifetimeManager(Type type)
{
    return IsNotificationHandler(type) ? new ContainerControlledLifetimeManager() : null;
}

static string GetName(Type type)
{
    return IsNotificationHandler(type) ? string.Format("HandlerFor" + type.Name) : string.Empty;
}
  • 开放式泛型:是,隐式的
  • 多重开放式泛型:是,可以使用用户构建的扩展功能
  • 泛型逆变性:derp(翻译不确定具体含义)

温莎(Windsor)

var container = new WindsorContainer();
container.Register(Classes.FromAssemblyContaining<IMediator>().Pick().WithServiceAllInterfaces());
container.Register(Classes.FromAssemblyContaining<Ping>().Pick().WithServiceAllInterfaces());
container.Kernel.AddHandlersFilter(new ContravariantFilter());
  • 开放式泛型:是的,隐式地
  • 多重开放式泛型:是的,隐式地
  • 泛型逆变性:是的,需要用户构建扩展

太好了!顺便说一下,上面的摘要漏掉了Windsor,但是在Jimmy的原始文章中可以找到。 - Louis
哇,之前没有人警告过这个 (: 我添加了 Windsor,谢谢 @Louis - stratovarius

21

嗨@abatishchev!:) ...最初的想法是确保第三方和内置方法处于同等地位;许多“注册”方法必须单独发货(例如MVC的RegisterControllers()),因此我认为围绕这种情况进行设计是值得的。(这是5年前设计的。) - Nicholas Blumhardt
1
@NicholasBlumhardt:你好!:) 很抱歉回复晚了,通知在其他信息中被遗漏了。实际上,这种一致性的追求对我来说很有意义。现在你怎么想?你会如何设计它? - abatishchev
@abatishchev 我不同意jgauffin的观点。扩展方法并不是封闭的,它们是扩展的。你编写框架的核心,可以完成所有应该完成的工作,而使用扩展方法提供一些额外的功能,可能是一些默认的帮助程序,但其他人可以自由地编写自己的扩展。我认为,如果你的框架“接受”扩展方法来扩展它,那么这是一个好的框架。 - t3chb0t

6

好的,经过搜索,我发现到目前为止最好的比较是:

这是一个于2010年3月份进行的调查。

值得注意的一点是,对于使用过DI/IoC框架并喜欢或不喜欢的人来说,StructureMap似乎是最优秀的选择之一。

此外从调查中可以看出,Castle.WindsorStructureMap似乎是最受青睐的选项。

有趣的是,UnitySpring.Net似乎是最不受欢迎的选项。(出于懒惰(和微软的支持),我曾考虑过Unity,但现在我会更仔细地研究Castle Windsor和StructureMap。)

当然,这可能(?)并不适用于于2010年5月发布的Unity 2.0。

希望其他人可以提供基于直接经验的比较。


2
Unity相当不错。它涵盖了大部分所需的功能,尽管有些人抱怨它不能解决循环依赖问题。但我喜欢它,因为它能满足我所有的需求。 - Dmitri Nesteruk
许多开发人员在使用Castle.Windsor时甚至不知道它。它是NHibernate的默认Ioc。(至少是我昨天下载的FluentNHibernate)。我还看到了一个使用LinFu的NHibernate实现。 - k3b

5

对于google code上的net-ioc-frameworks,包括linfu和spring.net在内,与本文同时书写时未列入您的列表中,请参考进行比较。

我曾经使用过spring.net:它有许多功能(aop、库、文档等),并且在dotnet和java-world中有很多使用经验。这些功能是模块化的,因此您不需要使用全部功能。这些功能是常见问题的抽象,如数据库抽象、日志记录抽象。但是,编写和调试IoC配置非常困难。

根据我目前的了解:如果我必须选择一个小型或中型项目,我会使用ninject,因为ioc配置可以在c#中完成并进行调试。但我还没有使用过它。对于大型模块化系统,我会继续使用spring.net,因为它具有抽象化库。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接