Методы расширения по сравнению с наследованием - PullRequest
39 голосов
/ 15 июля 2009

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

Спасибо!

Ответы [ 8 ]

31 голосов
/ 15 июля 2009

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

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

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

14 голосов
/ 15 июля 2009

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

Например, методы расширения Пропустить и взять.

8 голосов
/ 15 июля 2009

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

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

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

8 голосов
/ 15 июля 2009

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

редактировать

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

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

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

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

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

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

2 голосов
/ 15 июля 2009

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

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

Например:

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

Такие реализации великолепны, хотя технически они просто синтаксический сахар. ИМХО, все, что делает ваш код чище и более читабельным, прекрасно.

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

2 голосов
/ 15 июля 2009

Они очень разные, например, стандартные операторы запросов LINQ являются отличным примером методов расширения, которые должно быть сложно реализовать с наследованием, но если у вас есть доступ к классу и вы можете изменить источник, будет лучше использовать наследование, /> РЕДАКТИРОВАТЬ
и вот некоторые правила, которые я нахожу здесь C # 3.0 Особенности: Методы расширения

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

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

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

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


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


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

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

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


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

0 голосов
/ 14 августа 2012

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

...