Класс и класс контейнера метода расширения в том же пространстве имен. В чем выгода?

Я пытался NBuilder в моем модульном тесте. Отличная библиотека. Однако я не смог объяснить следующую структуру классов и интерфейсов.

  • В FizzWare.NBuilder Пространство имен:

    • ISingleObjectBuilder
    • SingleObjectBuilderExtensions
  • В FizzWare.NBuilder.Implementation

    • IObjectBuilder`
    • ObjectBuilder
  • SingleObjectBuilderExtensions это просто обертка на IObjectBuilder,

  • Код клиента обычно должен использовать класс с именем Builder который имеет статический метод, который дает вам ISingleObjectBuilder, Вам никогда не нужно создавать экземпляры каких-либо классов в клиентском коде.

Теперь я не понимаю SingleObjectBuilderExtensions, Дает ли это какое-либо преимущество в дизайне? Почему бы не методы прямо в ISingleObjectBuilder особенно когда два интерфейса находятся в одном пространстве имен.

2 ответа

Решение

ISingleObjectBuilder это интерфейс; интерфейсы не могут обеспечить реализацию. Это будет означать, что каждая реализация ISingleObjectBuilder нужно будет обеспечить реализацию.

Однако во многих случаях метод имеет предопределенное поведение, и ему просто необходим доступ к другим членам интерфейса (т. Е. Только к членам ISingleObjectBuilder), поэтому нет смысла заставлять каждую реализацию обеспечивать это.

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

Методы расширения решают обе эти проблемы:

  1. метод расширения будет работать для всех реализаций ISingleObjectBuilder
  2. он не меняет существующий 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.

Другие вопросы по тегам