【问题标题】:How is the intention of IServiceLocator.GetInstance(Type) different from the intention of IServiceProvider.GetService(Type)?IServiceLocator.GetInstance(Type) 的意图与 IServiceProvider.GetService(Type) 的意图有何不同?
【发布时间】:2013-01-29 14:55:59
【问题描述】:

方法签名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 缺少方法文档,但GetInstance(Type serviceType) 的 VS 对象浏览器中的摘要说该方法返回“请求的服务实例”。但是,文档 IServiceLocator 中也有一个异常条目,指出如果解析服务实例时出错,则应抛出 ActivationException

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

  • 如果缺少服务类型的实现,是否会导致IServiceLocator 实现中出现ActivationException 看起来不像。 IServiceLocatorimplementation template 忽略任何非空后置条件的概念。

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

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

【问题讨论】:

  • .NET IServiceProvider 的起源是 COM 的 IServiceProvider (msdn.microsoft.com/en-us/library/cc678965(v=vs.85).aspx)。它或多或少是这个想法的一个端口(请参阅相关的 COM 文档以了解有关原始意图的一些见解)。至于 ServiceLocator 之一,也许他们只是希望 GetInstance 重载具有相同的名称。 ServiceLocator 并不是真正的 .NET 框架的一部分。它是 Microsoft PAG 小组的插件。

标签: c# dependency-injection service-locator base-class-library common-service-locator


【解决方案1】:

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

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 类型时会返回任何内容?你可以这样实现它,但我不会称它为自动。

【讨论】:

  • 我在我的问题中解决了这个问题(阅读前四个要点)。此外,您的推理与提供的 IServiceLocator 的默认实现冲突
  • @smartcaveman:那是基于你的要点。您提到一个说没有结果该怎么办,而另一个提到出错时该怎么办。不,你在哪里说没有结果是错误。
  • 因此,关于 ActivationException 与 null 结果,我对您的帖子的解释是否正确,即错误情况和默认情况之间没有对应关系,我不应该对他们的通信?
  • @smartcaveman:鉴于所有示例中实现的统一性,我愿意。如果实现是不同的,那么我就不一定会得出这个结论。
  • 我认为这很公平。我想我的问题实际上归结为询问IServiceLocatorIEnumerable<T> 之间是否应该存在必需的递归关系,我现在看到这是在解释接口时需要进行的一个相当大的概念跳跃。我想我可能是从知道 IoC 容器倾向于递归工作时想到的,但这显然是一个实现细节(不是接口的特性)。
【解决方案2】:

正如您已经指出的,IServiceProvider.GetServiceIServiceLocator.GetInstance 之间的区别在于,任何 IServiceProvider.GetService 实现应该在服务未注册或无法注册时返回 null无论出于何种原因都可以解决,而另一方面,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 Blockthis thread的评论可以看出:

听起来我们可能在这里搞砸了。扔一个的想法 例外是我们都同意的明确设计目标。进行中 实现 IServiceProvider 更方便,但听起来像 这被忽略了。

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

【讨论】:

  • 我们应该怎么做?
  • 在 CSL 论坛上对此进行投诉并添加问题。但问题是,修复错误本身就是一个重大变化。只有一个解决方案:不要使用 CLS。不在您的 LOB 中,不在可重用的库中。
  • 我们当然可以责怪所有的 CSL 适配器,但是直到今天我仍然不知道为什么 MSFT 的 clr 开发人员没有采用 java throws 子句作为方法签名,那会成功的明确这些方法的真正意义是什么。我知道文档在那里,但大多数时候人们并不关心。正如你所说,这是违反 Liskov 的,所以在这一点上,考虑到不同实现的状态,你认为仍然值得使用它吗? Commons.Logging,似乎做得恰到好处。
  • 如果需要 return-null 行为,您可以将 IServiceProvider.GetService 包装在帮助程序类中,然后 try-catch + return-null。辅助类将与任何更改向前兼容。
猜你喜欢
  • 2018-09-09
  • 1970-01-01
  • 2011-09-10
  • 2011-07-17
  • 1970-01-01
  • 2010-12-08
  • 2017-09-30
  • 1970-01-01
  • 2020-09-06
相关资源
最近更新 更多