11

IServiceProvider.GetService(Type serviceType)方法签名和的意图有区别IServiceLocator.GetInstance(Type serviceType)吗?如果有,有什么区别?

我一直将它们视为等效的,但为了保持一致性,我选择使用单一方法。这似乎是处理这两个接口的一个足够好的解决方案,但我真的很想知道它们的实际用途是什么,以便我可以确定我在正确的地方使用了正确的接口。 如果他们的意图实际上是相同的,那么有任何理由为同一目的使用多组语义吗?(我知道签名是在 开始时推荐的GetInstanceMicrosoft.Practices.ServiceLocation但这似乎并不是引入重复的合理理由)。

为什么我很困惑

下面列出了我在试图找到这个问题的答案时发现的有时相互矛盾的事实,以及我对它的解释。我将这些包括在内,以便可以在有关该主题的所有已知信息的背景下解决我的问题。

  • MSDN 文档IServiceProvider说该方法GetService(Type serviceType)应该返回

    serviceType 类型的服务对象。
    -或 -如果没有serviceType类型的服务对象,则为
    null
  • MSDN 文档IServiceLocator缺少方法文档,但 VS 对象浏览器中的摘要说GetInstance(Type serviceType)该方法返回“请求的服务实例”。但是,文档中还有一个异常条目IServiceLocator,指出ActivationException如果解析服务实例时出现错误,则应抛出一个异常条目。

  • ActivationException位于Microsoft.Practices.ServiceLocation引入多年后IServiceProvider引入的命名空间中。因此,可以理解的IServiceProvider是 不指异常。话虽如此,IServiceLocator接口的文档没有说明null如果没有找到结果就返回。也不清楚是否缺少所请求的服务类型的实现应构成例外。

  • 缺少服务类型的实现是否会导致ActivationExceptioninIServiceLocator实现? 看起来不像。的实现模板IServiceLocator忽略任何非空后置条件的概念。

  • 的实现模板IServiceLocator也被IServiceProvider.GetService(Type)视为IServiceLocator.GetInstance(). 这是否算作违反 Liskov(由于在未在基类型上声明的子类型中抛出异常),或者,这实际上需要实现差异而不是接口方法签名上声明的异常?我的意思是:我们确定ServiceLocatorImplBase实现模板IServiceLocator正确实现了这两个接口吗?IServiceProvider将调用包装GetInstance在 try 块中并null在捕获到异常时返回 是否会更好地表示接口的意图?

  • 附录:与此相关的另一个问题是 to 的对应IServiceLocator.GetAllInstances(Type)关系IServiceLocator.GetInstance(Type)。具体来说,对于任何类型 T,其实现是否应该IServiceLocator.GetAllInstances(typeof(T))返回与 ? 相同的结果IServiceLocator.GetInstance(typeof(IEnumerable<>).MakeGenericType(typeof(T)) (很容易看出这与IServiceProvider对应关系有什么关系,但我认为最好保持问题简单,只比较这种情况下同一接口的两种方法。)

4

2 回答 2

11

IServiceProvider.GetService正如您已经指出的那样,和之间的区别在于IServiceLocator.GetInstance,任何IServiceProvider.GetService实现都应该null在服务未注册或由于任何原因无法解决时返回,而IServiceLocator.GetInstance另一方面,实现应该在这种情况下抛出异常(并且永远不会回来null)。

但请注意我使用了“应该”这个词。Common Service Locator 项目(拥有该接口)附带的所有 CSL 适配器(用于 Windsor、Spring、Unity 和 StructureMap 等)IServiceLocator都不遵守该接口,并且当您调用它们的方法IServiceProvider时它们会抛出异常。IServiceProvider.GetService

通过违反合同,CSL 的设计者设法使IServiceProvider界面变得毫无用处。您现在根本不能再依赖它来返回 null,这很糟糕。特别糟糕。我所知道的唯一符合合同的 CSL 适配器是Simple Injector 适配器,但是由于所有其他实现都被破坏了,即使这个正确实现的适配器在那时也没用,因为您无法安全地交换实现。

这算不算违反 Liskov

绝对地。他们打破了接口契约,实现不能相互替代。

设计师们知道这一点,从Glenn Block对此线程的评论中可以看出:

听起来我们可能在这里搞砸了。抛出异常的想法是我们都同意的明确设计目标。让它实现 IServiceProvider 更方便,但听起来这被忽略了。

该漏洞从未得到修复,因为 CSL 从未更新过。

于 2013-02-18T11:54:10.417 回答
3

我认为您没有提到的两种设计之间存在区别:

IServiceProvider.GetService
如果没有 serviceType 类型的服务对象,则为 null。


如果解析服务实例时出错,则应引发IServiceLocator.GetInstance ActivationException。

重要的是要注意一个是指定默认情况,而另一个是指定错误情况。他们不一样是有道理的。

从提供的示例来看,该组合似乎是预期的实现。默认为 null,错误时包裹在 ActivationException 中。

编辑:

具体来说,对于任何类型 T,IServiceLocator.GetAllInstances(typeof(T)) 的实现是否应该返回与 IServiceLocator.GetInstance(typeof(IEnumerable<>).MakeGenericType(typeof(T)) 相同的结果?

不能typeof(IEnumerable<T>)作为更紧凑的形式工作吗?此外,为什么GetInstance在要求输入IEnumerable类型时会返回任何内容?你可以这样实现它,但我不会称它为自动的。

于 2013-02-13T22:21:59.393 回答