Как намерение 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
? Вы могли бы реализовать его таким образом, но я бы не назвал его автоматическим.