Подтвердить что ты не робот

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

Существуют ли правила большого пальца, которые помогают определить, что использовать в каком случае? Должен ли я предпочитать один за другой в большинстве случаев?

Спасибо!

4b9b3361

Ответ 1

Методы расширения полезны, но их сложнее обнаружить через среду IDE, чем обычные методы, поскольку они не привязаны к исходному классу, и нет никаких указаний относительно того, где может существовать код для них. Есть несколько рекомендации по лучшей практике относительно того, где их разместить и как их назвать, но это только рекомендации, и нет никакой гарантии, что кто-то будет следуйте им.

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

Использование наследования позволит вам добавлять, удалять или переопределять функциональные возможности и гарантировать, что он всегда присутствует с классом при его создании.

Ответ 2

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

Например, методы расширения Skip и Take.

Ответ 3

Ну... вы не всегда можете использовать наследование. String, например, является закрытым классом. Это в тех случаях, когда действительно расширяется метод расширения.

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

Еще одно отличное место для методов расширения - против перечислений. Я почти всегда включаю метод расширения HasFlag в отношении любых создаваемых ячеек [Flags].

Ответ 4

По возможности используйте наследование вместо методов расширения.

изменить

Я предпочитаю держать это коротко и просто, но я, конечно, отвечу на последующие вопросы.

В случаях, когда наследование возможно, то есть классы, которые не запечатаны, это почти всегда лучший вариант, чем методы расширения. Фактически, это то, что документ о лучшей практике, о котором говорится в заявлении womp. В нем есть заголовки, такие как "Будьте осторожны с методами расширения", "Подумайте дважды, прежде чем расширять типы, которыми вы не являетесь", и "Предпочитайте расширения интерфейса над расширениями классов". Другими словами, он просто говорит, что делал мой однострочный лайнер, более подробно.

В статье приводятся подробные причины, но суть в том, что именно так были разработаны методы расширения. Они были добавлены к языку в конце игры как немного синтаксического сахара, чтобы позволить MS вклиниваться в LINQ, не возвращаясь и не изобретая велосипед. Это канонический пример того, для чего они хороши. Другим хорошим примером является добавление методов утилиты, таких как:

public static string FormatWith(this string format, params object[] args)
{ return string.Format(CultureInfo.InvariantCulture, format, args); }

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

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

Ответ 5

Они очень разные, например, стандартные операторы запросов LINQ - отличный пример методов расширения, которые трудно реализовать с наследованием, но если у вас есть доступ к классу и вы можете изменить источник, лучше использовать наследование,
EDIT
и вот некоторые правила, которые я нахожу здесь Возможности С# 3.0: методы расширения

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

Ответ 6

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

У методов расширения есть одна действительно отличная функция, которая является неотъемлемой частью их реализации. Поскольку они являются статическими методами, вы можете вызвать их на нулевом объекте.

Например:

string a = null;
return a.IfNullOrEmpty("Default Value");

Реализации, подобные этому, великолепны, хотя они являются технически просто синтаксическим сахаром. ИМХО, все, что держит ваш код чище и читаемо, отлично.

Хотя мне это не нравится, они на самом деле не доступны. Если я скопирую этот код из одного класса в другой, мне придется искать пространство имен, в котором оно было определено.

Ответ 7

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

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

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


LinqPad использует методы расширения, например метод .Dump(), с помощью которого вы можете сбросить (распечатать) содержимое каждого вида объекта в окне вывода.


.NET framework сам использует методы расширения во многих местах, например, в Linq:

public static TSource FirstOrDefault<TSource>(this 
                        System.Collections.Generic.IEnumerable<TSource> source)

который возвращает первый элемент или значение по умолчанию любой перечисляемой коллекции любого типа объекта.


Пример, где методы расширения лучше, чем наследование, следующие: Предположим, вы хотите создать метод, который способен создать clone (копировать) любого существующего объекта. С расширениями (и дженериками, плюс отражение) вы можете сделать это таким образом.

Ответ 8

Удлинительные методы ломают хорошую конструкцию OO. Говорить, что они должны использоваться на запечатанных классах, у которых нет доступа к базе кода, смешно. Классы, которые запечатаны и у вас нет доступа, вероятно, запечатаны по какой-либо причине (производительность, безопасность потоков) и слепать по функциональности слепо на этих классах, это опасно. Всегда существует способ реализации шаблона декоратора чистым способом OO и не делать этого таким образом, что делает код более сложным для чтения, обслуживания и рефакторинга. Как правило, если свойство языка плохо пахнет, его следует избегать. Я уверен, что вы можете найти один пример, где методы расширения полезны, но правда в том, что эти функции будут злоупотреблять этими разработчиками с минимальным обучением OO.