Класс и класс контейнера метода расширения в том же пространстве имен. В чем выгода?
Я пытался NBuilder в моем модульном тесте. Отличная библиотека. Однако я не смог объяснить следующую структуру классов и интерфейсов.
В
FizzWare.NBuilder
Пространство имен:ISingleObjectBuilder
SingleObjectBuilderExtensions
В
FizzWare.NBuilder.Implementation
- IObjectBuilder`
ObjectBuilder
SingleObjectBuilderExtensions
это просто обертка наIObjectBuilder
,Код клиента обычно должен использовать класс с именем
Builder
который имеет статический метод, который дает вамISingleObjectBuilder
, Вам никогда не нужно создавать экземпляры каких-либо классов в клиентском коде.
Теперь я не понимаю SingleObjectBuilderExtensions
, Дает ли это какое-либо преимущество в дизайне? Почему бы не методы прямо в ISingleObjectBuilder
особенно когда два интерфейса находятся в одном пространстве имен.
2 ответа
ISingleObjectBuilder
это интерфейс; интерфейсы не могут обеспечить реализацию. Это будет означать, что каждая реализация ISingleObjectBuilder
нужно будет обеспечить реализацию.
Однако во многих случаях метод имеет предопределенное поведение, и ему просто необходим доступ к другим членам интерфейса (т. Е. Только к членам ISingleObjectBuilder
), поэтому нет смысла заставлять каждую реализацию обеспечивать это.
Кроме того, не стоит добавлять дополнительные элементы в существующий интерфейс, поскольку это будет серьезным изменением для всех существующих реализаций.
Методы расширения решают обе эти проблемы:
- метод расширения будет работать для всех реализаций
ISingleObjectBuilder
- он не меняет существующий API, поэтому все существующие реализации будут продолжать действовать
Наличие этого в том же пространстве имен просто делает это удобным. Вероятно, что любой код, использующий ISingleObjectBuilder
уже есть using
директива, импортирующая это пространство имен; поэтому большая часть кода уже увидит метод расширения в IDE, просто нажав .
в IDE.
Чтобы добавить конкретный пример, LINQ-to-Objects работает на IEnumerable<T>
, Есть много IEnumerable<T>
Реализации. Если каждый из них должен был написать свой First(...)
, FirstOrDefault(...)
, Any(...)
, Select(...)
и т. д., это было бы огромным бременем - бот не принесет никакой пользы, поскольку реализации в большинстве случаев были бы в значительной степени идентичны. Кроме того, установка этого на интерфейс ретроспективно была бы катастрофической.
В качестве примечания: версии метода для каждого типа всегда имеют приоритет над методами расширения, поэтому если (в случае LINQ-to-Objects) у вас есть тип, который реализует IEnumerable<T>
(для некоторых T
), и этот тип имеет .First()
метод экземпляра, то:
YourType foo = ...
var first = foo.First();
будет использовать вашу версию, а не методы расширения.
Методы расширения являются только синтаксическим сахаром. Это обычные статические методы, которые работают с общедоступными данными, которые они "расширяют".
Кажется, нет необходимости в том, чтобы эти методы расширения работали с закрытыми полями, и это также дает вам возможность иметь интерфейс вместо абстрактного класса. И все знают, что интерфейсы - лучший выбор, чем абстрактные классы.
И причина того, что оба находятся в одном и том же пространстве имен, состоит в том, чтобы не объявлять новое пространство имен просто для использования методов расширения. Сколько раз с вами случалось, что вы пытались использовать LINQ только для того, чтобы заметить, что в intellisense нет методов или код не компилировался. Причиной тому было отсутствие пространства имен System.Linq.