Как намерение IServiceLocator.GetInstance(Type) отличается от намерения IServiceProvider.GetService(Тип)?

Есть ли разница в намерениях сигнатур метода IServiceProvider.GetService(Type serviceType) и IServiceLocator.GetInstance(Type serviceType)? Если да, то какое это различие?

Я всегда рассматривал их как эквивалентные, но сделал выбор, чтобы использовать один метод для согласованности. Это похоже на достаточно хорошее решение для работы с двумя интерфейсами, но я действительно хотел бы знать, как их использование было на самом деле предназначено, чтобы я мог быть уверен, что использую правильный вариант в нужном месте. Если их намерение на самом деле то же самое, то есть ли какая-либо причина наличия нескольких наборов семантики с той же целью? (я понимаю, что подпись GetInstance была рекомендована в начале Microsoft.Practices.ServiceLocation, но на самом деле это не кажется разумной причиной для введения дублирования).

Почему я запутался

Ниже приведен список иногда противоречивых фактов, которые я нашел, пытаясь найти ответ на этот вопрос, а также мою интерпретацию. Я включаю их, чтобы мой вопрос мог быть рассмотрен в контексте всей информации, которая уже известна об этой теме.

  • документация MSDN для IServiceProvider говорит, что метод GetService(Type serviceType) должен возвращать

    Объект службы типа serviceType.

    -или-
    null, если нет объекта службы типа serviceType.
  • документация MSDN для IServiceLocator отсутствует документация по методу, но резюме в обозревателе объектов VS GetInstance(Type serviceType) говорит, что метод возвращает "запрошенный экземпляр службы". Однако в документации IServiceLocator имеется также запись об исключении, в которой говорится, что при возникновении ошибки в экземпляре службы должен быть выброшен ActivationException.

  • ActivationException находится в пространстве имен Microsoft.Practices.ServiceLocation, который был введен через несколько лет после введения IServiceProvider. Таким образом, понятно, что IServiceProvider не относится к исключению. При этом в документации по интерфейсу IServiceLocator ничего не говорится о возврате null, если результат не найден. Также неясно, является ли исключение внедрением запрашиваемого типа услуги.

  • Если отсутствие реализации для типа службы вызывает реализацию ActivationException в IServiceLocator? Это не похоже на это. Шаблон для IServiceLocator игнорирует любую концепцию непустого пост-состояния.

  • шаблон реализации для IServiceLocator также рассматривает IServiceProvider.GetService(Type) как альтернативный синтаксис для IServiceLocator.GetInstance(). Означает ли это как нарушение Лискова (из-за исключения исключения в подтипе, который не объявлен в базовом типе), или, действительно ли это потребует разницы в реализации, а не в исключениях, объявленных в сигнатурах метода интерфейса? Я уверен, что шаблон реализации ServiceLocatorImplBase для IServiceLocator правильно реализует оба интерфейса? Было бы лучше отображать намерения интерфейсов для IServiceProvider для обертывания вызова GetInstance в блоке try и возврата null при обнаружении исключения?

  • Добавление: Еще одна проблема, связанная с этим, - это соответствие IServiceLocator.GetAllInstances(Type) - IServiceLocator.GetInstance(Type). В частности, Для любого типа T, если реализация IServiceLocator.GetAllInstances(typeof(T)) возвращает тот же результат, что и IServiceLocator.GetInstance(typeof(IEnumerable<>).MakeGenericType(typeof(T))? (легко видеть, как это относится к корреспонденции IServiceProvider, но я думаю, что это лучше оставить вопрос простым и сравнить только два метода одного и того же интерфейса для этого случая.)

Ответ 1

Как вы уже отметили, разница между IServiceProvider.GetService и IServiceLocator.GetInstance заключается в том, что любая реализация IServiceProvider.GetService должна возвращать null, когда служба не зарегистрирована или когда ее невозможно решить по какой-либо причине, в то время как реализации IServiceLocator.GetInstance с другой стороны должны вызывать исключение в этом случае (и никогда не возвращать null).

Но обратите внимание на то, что я использую слово "должен". Все CSL-адаптеры (для Windsor, Spring, Unity и StructureMap и т.д.), Которые поставляются с проектом Common Service Locator (которому принадлежит IServiceLocator) не придерживаются интерфейса IServiceProvider, и они вызывают исключение, когда вы вызываете их метод IServiceProvider.GetService.

Разбив контракт, разработчикам CSL удалось сделать интерфейс IServiceProvider абсолютно бесполезным. Теперь вы просто не можете полагаться на него, чтобы вернуть нуль больше, что плохо. Действительно плохо. Единственным адаптером CSL, который я знаю об этом, является Simple Injector adapter, но поскольку все другие реализации не работают, даже этот правильно реализованный адаптер в этот момент бесполезен, так как вы не можете безопасно заменять реализации.

Означает ли это как нарушение Лискова

Совершенно верно. Они нарушили контракт интерфейса, и реализации не могут быть заменены друг на друга.

Дизайнеры знают об этом, как видно из Glenn Block на этот поток:

Похоже, мы, возможно, перепутали здесь. Идея бросить Исключение было явной целью проекта, о которой мы все договорились. Делая это реализовать IServiceProvider было более удобным, но звучит как это было упущено.

Ошибка никогда не была исправлена, поскольку CSL никогда не обновлялся.

Ответ 2

Я думаю, что существует различие между двумя проектами, которые вы не упомянули:

IServiceProvider.GetService
null, если нет объекта службы типа serviceType.

IServiceLocator.GetInstance
При возникновении ошибки в экземпляре службы следует вызывать исключение ActivationException.

Важно отметить, что один указывает случай по умолчанию, а другой указывает на ошибку. Имеет смысл, что они не будут одинаковыми.

Из предоставленных образцов выглядит так, что комбинация является ожидаемой реализацией. null по умолчанию, обернуть в ActivationException при ошибке.

EDIT:

В частности, для любого типа T, если реализация IServiceLocator.GetAllInstances(typeof (T)) возвращает тот же результат, что и IServiceLocator.GetInstance(typeof (IEnumerable < > ). MakeGenericType (typeof (T))?

Не будет typeof(IEnumerable<T>) работать как более компактная форма? Кроме того, почему GetInstance возвращает что-либо при запросе типа IEnumerable? Вы могли бы реализовать его таким образом, но я бы не назвал его автоматическим.