方法签名IServiceProvider.GetService(Type serviceType)
和IServiceLocator.GetInstance(Type serviceType)
的意图是否存在差异?如果是这样,区别是什么?
我一直认为它们是等价的,但我们选择使用单一方法来保持一致性。对于处理这两个接口来说,这似乎是一个很好的解决方案,但我真的很想知道它们的用法是如何实际的,这样我才能确定我在正确的位置使用正确的用法。 如果他们的意图实际上是相同的,那么是否有任何理由为同一目的设置多组语义?(我理解the GetInstance
signature was recommended during the inception of Microsoft.Practices.ServiceLocation
,但这看起来并不像引入复制的合理理由。
以下是我在试图找到这个问题的答案时发现的有时相互矛盾的事实清单,以及我对此的解释。我将这些问题包括在内,以便可以在已知有关该主题的所有信息的背景下解决我的问题。
MSDN documentation for IServiceProvider
表示GetService(Type serviceType)
方法应返回
类型为 serviceType 的服务对象。
- 或 -
如果没有类型为 serviceType 的服务对象,则 null 。
MSDN documentation for IServiceLocator
缺少方法文档,但VS对象浏览器中GetInstance(Type serviceType)
的摘要表明该方法返回“请求的服务实例”。但是,文档IServiceLocator
中还有一个例外条目,表示如果解析服务实例时出错,则应抛出ActivationException
。
ActivationException
位于引入Microsoft.Practices.ServiceLocation
多年后引入的IServiceProvider
命名空间中。因此,IServiceProvider
没有引用异常是可以理解的。话虽这么说,IServiceLocator
接口的文档没有说明如果没有找到结果就返回null
。还不清楚是否缺乏所要求的服务类型的实施应构成例外。
如果缺少服务类型的实现会导致ActivationException
实施中的IServiceLocator
吗?它看起来不像。 IServiceLocator
IServiceLocator
忽略任何非空后置条件的概念。
IServiceProvider.GetService(Type)
的{{3}}也将IServiceLocator.GetInstance()
视为ServiceLocatorImplBase
的替代语法。这是否违反Liskov(由于在基类型上未声明的子类型中抛出异常),或者,实际上是否需要在实现中有所不同,而不是在接口的方法签名上声明的异常?我得到的是:我们确定IServiceLocator
的{{1}}实现模板是否正确实现了两个接口?它是否更好地表示接口的意图IServiceProvider
将GetInstance
调用包装在try块中,并在发现异常时返回null
?
附录:与此相关的另一个问题是IServiceLocator.GetAllInstances(Type)
与IServiceLocator.GetInstance(Type)
的对应关系。具体来说,对于任何类型,T,IServiceLocator.GetAllInstances(typeof(T))
的实现应该返回与IServiceLocator.GetInstance(typeof(IEnumerable<>).MakeGenericType(typeof(T))
相同的结果吗?(很容易看出它与IServiceProvider
的关系对应,但我认为最好保持问题简单,只比较同一界面的两种方法。)
答案 0 :(得分:11)
正如您已经注意到的,IServiceProvider.GetService
和IServiceLocator.GetInstance
之间的区别在于,当服务不是时,任何IServiceProvider.GetService
实现应该返回null
t注册或无法解决的原因,而另一方面IServiceLocator.GetInstance
实现应该在这种情况下抛出异常(并且永远不会返回null
)。
但请注意我使用“应该”这个词。 Common Service Locator project(拥有IServiceLocator
接口)附带的所有CSL适配器(适用于Windsor,Spring,Unity和StructureMap等)不符合IServiceProvider
接口和当你调用他们的IServiceProvider.GetService
方法时,他们会抛出异常。
通过违反合同,CSL的设计人员设法使IServiceProvider
界面毫无用处。你现在根本不能再依赖它来返回null,这很糟糕。特别糟糕。我知道唯一符合合同的CSL适配器是Simple Injector adapter,但由于所有其他实现都被破坏,即使这个正确实现的适配器在那时也没用,因为你无法安全地交换实现。
这是否违反Liskov
绝对。他们打破了接口合同,实现无法互相替换。
设计师知道这一点,从Glenn Block对this thread的评论中可以看出:
听起来我们可能搞砸了。投掷的想法 例外是我们都同意的明确的设计目标。进行中 实现IServiceProvider更方便,但听起来像 这被忽视了。
该缺陷从未得到修复,因为CSL从未更新过。
答案 1 :(得分:3)
我认为你没有提及的两个设计有所区别:
IServiceProvider.GetService
如果没有serviceType类型的服务对象,则返回null。IServiceLocator.GetInstance
如果解析服务实例时出错,则应抛出ActivationException。
重要的是要注意一个是指定默认情况,而另一个是指定错误情况。它们不一样是有意义的。
从提供的样本看起来组合是预期的实现。 null作为默认值,在出错时包装在ActivationException中。
编辑:
具体来说,对于任何类型,T,应该是IServiceLocator.GetAllInstances(typeof(T))的实现返回与IServiceLocator.GetInstance(typeof(IEnumerable&lt;&gt;))相同的结果.MakeGenericType(typeof(T))?< / p>
typeof(IEnumerable<T>)
不会更紧凑吗?另外,为什么GetInstance
会在被要求IEnumerable
类型时返回任何内容?你可以这样实现它,但我不会自动调用它。