IServiceLocator.GetInstance(Type)与IServiceProvider.GetServic
本文关键字:IServiceProvider GetServic Type GetInstance IServiceLocator | 更新日期: 2023-09-27 17:59:27
方法签名IServiceProvider.GetService(Type serviceType)
和IServiceLocator.GetInstance(Type serviceType)
的意图有区别吗?如果是,区别是什么?
我一直把它们视为等价的,但为了保持一致性,我选择了使用单一的方法。对于处理这两个接口来说,这似乎是一个足够好的解决方案,但我真的很想知道它们的实际用途,这样我就可以确保在正确的地方使用正确的接口如果它们的意图实际上是相同的,那么有什么理由为同一目的使用多组语义吗(我知道GetInstance
签名是在Microsoft.Practices.ServiceLocation
开始时推荐的,但这似乎并不是引入重复的合理理由)。
为什么我感到困惑
以下是我在试图找到这个问题的答案时发现的一些有时相互矛盾的事实,以及我对这些事实的解释。我将这些内容包括在内,以便我的问题能够在有关该主题的所有已知信息的背景下得到解决。
-
MSDN中
IServiceProvider
的文档指出GetService(Type serviceType)
方法应该返回类型为serviceType的服务对象
-或-
如果没有serviceType类型的服务对象,则为null。 -
IServiceLocator
的MSDN文档缺少方法文档,但GetInstance(Type serviceType)
的VS对象浏览器中的摘要显示该方法返回"请求的服务实例"。但是,文档IServiceLocator
中也有一个异常条目,指出如果解析服务实例时出错,则应抛出ActivationException
。 -
ActivationException
位于Microsoft.Practices.ServiceLocation
名称空间中,该名称空间是在IServiceProvider
引入多年后引入的。因此,IServiceProvider
不引用异常是可以理解的。也就是说,IServiceLocator
接口的文档没有说明如果找不到结果,将返回null
。也不清楚缺少所请求服务类型的实现是否应该构成异常。 -
缺少服务类型的实现是否会导致
IServiceLocator
实现中出现ActivationException
它看起来不像。IServiceLocator
的实现模板忽略了任何非null post条件的概念。 -
CCD_ 20的实现模板还将CCD_ 21作为CCD_。这算不算违反Liskov(由于在子类型中抛出未在基类型上声明的异常),或者,这实际上需要实现中的差异,而不是在接口的方法签名上声明的例外?我的意思是:我们确定
IServiceLocator
的ServiceLocatorImplBase
实现模板正确地实现了这两个接口吗对于IServiceProvider
来说,将GetInstance
调用封装在try块中,并在捕捉到异常时返回null
,这会更好地表示接口的意图吗? -
附录:与此相关的另一个问题是
IServiceLocator.GetAllInstances(Type)
与IServiceLocator.GetInstance(Type)
的对应关系。具体来说,对于任何类型T,IServiceLocator.GetAllInstances(typeof(T))
的实现是否应该返回与IServiceLocator.GetInstance(typeof(IEnumerable<>).MakeGenericType(typeof(T))
相同的结果(很容易看出这与IServiceProvider
对应关系,但我认为最好保持问题简单,在这种情况下只比较同一接口的两种方法。)
正如您已经注意到的,IServiceProvider.GetService
和IServiceLocator.GetInstance
之间的区别在于,当服务未注册或由于任何原因无法解决时,任何IServiceProvider.GetService
实现都应该返回null
,而IServiceLocator.GetInstance
实现在这种情况下应该抛出异常(永远不要返回null
)。
但请注意我使用了"应该"这个词。公共服务定位器项目(拥有IServiceLocator
接口)附带的所有CSL适配器(用于Windsor、Spring、Unity和StructureMap等)都不遵守IServiceProvider
接口,并且当您调用它们的IServiceProvider.GetService
方法时,它们会抛出异常。
CSL的设计者违反了合同,使IServiceProvider
接口变得毫无用处。您现在不能再依赖它返回null了,这很糟糕。真的很糟糕。据我所知,唯一遵守合同的CSL适配器是Simple Injector适配器,但由于所有其他实现都已损坏,因此即使是这个正确实现的适配器在这一点上也毫无用处,因为您无法安全地交换实现。
这算不算违反利斯科夫
当然。它们破坏了接口契约,实现不能相互替代。
设计师们知道这一点,可以从Glenn Block对这个帖子的评论中看出:
听起来我们可能搞砸了。抛出例外是一个明确的设计目标,我们都同意实现IServiceProvider更方便,但听起来这一点被忽视了。
该漏洞从未被修复,因为CSL从未更新过。
我认为你没有提到的两种设计之间有一个区别:
IServiceProvider.GetService
如果没有serviceType类型的服务对象,则为null。IServiceLocator.GetInstance
如果解析服务实例时出错,则应引发ActivationException。
需要注意的是,一个指定默认情况,另一个指定错误情况。它们不一样是有道理的。
从提供的示例来看,组合似乎是预期的实现。null作为默认值,出现错误时包装ActivationException。
编辑:
具体来说,对于任何类型T,IServiceLocator.GetAllInstances(typeof(T))的实现是否应该返回与IServiceLocator.GetInstance(typeof?
typeof(IEnumerable<T>)
不是更紧凑的形式吗?此外,当要求IEnumerable
类型时,为什么GetInstance
会返回任何内容?你可以用这种方式实现它,但我不认为它是自动的。