«Неверующий» в C # спрашивал меня, какова цель методов расширения. Я объяснил, что затем вы можете добавить новые методы к уже определенным объектам, особенно если вы не владеете / не управляете источником исходного объекта.
Он поднял вопрос: «Почему бы просто не добавить метод в свой класс?» Мы ходим кругом (в хорошем смысле). Мой общий ответ состоит в том, что это еще один инструмент в наборе инструментов, и он отвечает, что это бесполезная трата инструмента ... но я думал, что получу более «просвещенный» ответ.
В каких сценариях вы использовали методы расширения, которые не могли (или не должны были использовать) использовать метод, добавленный в ваш собственный класс?
c#
extension-methods
Джерри
источник
источник
Extensions.To(1, 10)
не имеет смысла и1.To(10)
носит описательный характер. Конечно, я понимаю техническую сторону, о которой вы говорите. На самом деле есть сценарии, в которых « нельзя » использовать подход расширения вместо статических методов, например, отражение, другой случайdynamic
.Ответы:
Я думаю, что методы расширения очень помогают при написании кода, если вы добавите методы расширения к базовым типам, вы быстро получите их в intellisense.
У меня есть поставщик формата для форматирования файла . Чтобы использовать его, мне нужно написать:
Console.WriteLine(String.Format(new FileSizeFormatProvider(), "{0:fs}", fileSize));
Создавая метод расширения, я могу написать:
Чище и проще.
источник
LongToFileSize(fileSize)
, что столь же кратко и, возможно, столь же ясно.Только преимущество методов расширения является читабельность кода. Вот и все.
Методы расширения позволяют это делать:
вместо этого:
Теперь в C # есть много подобных вещей. Другими словами, в C # есть много функций, которые кажутся тривиальными и не приносят большой пользы сами по себе. Однако как только вы начнете комбинировать эти функции вместе, вы начнете видеть что-то немного большее, чем сумма его частей. LINQ значительно выигрывает от методов расширения, поскольку без них запросы LINQ были бы почти нечитаемыми. LINQ был бы возможен без методов расширения, но не практично.
Методы расширения очень похожи на частичные классы C #. Сами по себе они не очень полезны и кажутся тривиальными. Но когда вы начинаете работать с классом, которому нужен сгенерированный код, частичные классы начинают иметь гораздо больший смысл.
источник
Не забывайте инструменты! Когда вы добавляете метод расширения M к типу Foo, вы получаете «M» в списке intellisense Foo (при условии, что класс расширения входит в область видимости). Это значительно упрощает поиск "M", чем MyClass.M (Foo, ...).
В конце концов, это просто синтаксический сахар для статических методов в другом месте, но как покупка дома: «местоположение, местоположение, местоположение!» Если на типе зависнет, люди найдут!
источник
String
), которые идут с довольно длинным списком методов экземпляра, поэтому эта проблема не является специфической для методов расширения.Еще два преимущества методов расширения, с которыми я столкнулся:
Select(...)
,Where(...)
и т. Д. Повешенные наIEnumerable<T>
интерфейсе.источник
Некоторые из лучших применений методов расширения, которые я использовал, - это способность:
sealed
.Возьмем, например
IEnumerable<T>
,. Хотя в нем много методов расширения, меня раздражало то, что в нем не реализован общий метод ForEach. Итак, я сделал свой:public void ForEach<T>(this IEnumerable<T> enumerable, Action<T> action) { foreach ( var o in enumerable ) { action(o); } }
Вуаля, все мои
IEnumerable<T>
объекты, независимо от типа реализации, и независимо от того, написал ли я его или кто-то другой, теперь имеютForEach
метод, добавив соответствующий оператор using в мой код.источник
Одна из веских причин для использования методов расширения - это LINQ. Без методов расширения многое из того, что вы можете делать в LINQ, было бы очень трудным. Методы расширения Where (), Contains (), Select означают, что к существующим типам добавляется гораздо больше функций без изменения их структуры.
источник
Есть множество ответов о преимуществах методов расширения; как насчет устранения недостатков ?
Самым большим недостатком является отсутствие ошибки или предупреждения компилятора, если у вас есть обычный метод и метод расширения с одинаковой сигнатурой в одном контексте.
Предположим, вы создали метод расширения, применяемый к определенному классу. Позже кто-то создает метод с идентичной подписью для самого этого класса.
Ваш код будет компилироваться, и вы даже можете не получить ошибку во время выполнения. Но вы больше не используете тот же код, что и раньше.
источник
Свободные интерфейсы и контекстная чувствительность, продемонстрированные Грегом Янгом на CodeBetter
источник
Мой личный аргумент в пользу методов расширения заключается в том, что они очень хорошо вписываются в дизайн ООП: рассмотрите простой метод
bool empty = String.IsNullOrEmpty (myString)
в сравнении с
bool empty = myString.IsNullOrEmpty ();
источник
Выше есть куча отличных ответов о том, какие методы расширения позволяют вам делать.
Мой короткий ответ: они почти исключают необходимость в заводах.
Я просто отмечу, что это не новая концепция, и одна из главных их проверок заключается в том, что они являются убийственной функцией в Objective-C ( категориях ). Они добавляют такую гибкость к разработке на основе фреймворков, что NeXT имеет в качестве основных пользователей NSA и специалистов по финансовому моделированию с Уолл-стрит.
REALbasic также реализует их как расширяющие методы, и они использовались там аналогичным образом, упрощая разработку.
источник
Я хотел бы поддержать здесь другие ответы, в которых упоминается улучшенная читаемость кода как важная причина использования методов расширения. Я продемонстрирую это на двух аспектах: объединение методов в цепочку и вызовы вложенных методов и загромождение запроса LINQ бессмысленными именами статических классов.
В качестве примера возьмем этот запрос LINQ:
numbers.Where(x => x > 0).Select(x => -x)
Оба
Where
иSelect
являются методами расширения, определенными в статическом классеEnumerable
. Таким образом, если бы методов расширения не существовало, а это были обычные статические методы, последняя строка кода, по сути, должна была бы выглядеть так:Enumerable.Select(Enumerable.Where(numbers, x => x > 0), x => -x)
Посмотрите, насколько отвратительнее стал этот запрос.
Во-вторых, если бы вы теперь захотели ввести свой собственный оператор запроса, у вас, естественно, не было бы способа определить его внутри
Enumerable
статического класса, как и все другие стандартные операторы запросов, потому что онEnumerable
находится в структуре, и вы не можете контролировать этот класс. Следовательно, вам нужно будет определить свой собственный статический класс, содержащий методы расширения. Затем вы можете получить такие запросы, как этот:Enumerable.Select(MyEnumerableExtensions.RemoveNegativeNumbers(numbers), x => -x) // ^^^^^^^^^^^^^^^^^^^^^^ // different class name that has zero informational value // and, as with 'Enumerable.xxxxxx', only obstructs the // query's actual meaning.
источник
Это правда, что вы можете добавить свой (расширенный) метод прямо в свой класс. Но не все классы написаны вами. Классы из основной библиотеки или сторонних библиотек часто закрыты, и было бы невозможно получить синтаксический сахар без методов расширения. Но помните, что методы расширения похожи на (статические) автономные методы, например. c ++
источник
Методы расширения также могут помочь сохранить ваши классы и их зависимости в чистоте. Например, вам может понадобиться метод Bar () для класса Foo везде, где используется Foo. Однако вам может понадобиться метод .ToXml () в другой сборке и только для этой сборки. В этом случае вы можете добавить необходимые зависимости System.Xml и / или System.Xml.Linq в эту сборку, а не в исходную сборку.
Преимущества: зависимости в вашей определяющей сборке класса сводятся только к самому необходимому, а другие потребляющие сборки не смогут использовать метод ToXml (). См. Эту презентацию PDC для получения дополнительной информации.
источник
Я согласен с тем, что методы расширения увеличивают читаемость кода, но на самом деле это не что иное, как статические вспомогательные методы.
IMO, использующий методы расширения для добавления поведения в ваши классы, может быть:
Сбивает с толку: программисты могут полагать, что методы являются частью расширенного типа, поэтому не понимают, почему методы исчезают, когда пространство имен-расширений не импортируется.
Антипаттерн: вы решаете добавить поведение к типам в своей структуре, используя методы расширения, а затем отправляете их какому-то человеку, который выполняет модульное тестирование. Теперь он застрял в среде, содержащей кучу методов, которые он не может подделать.
источник
Методы расширения на самом деле являются воплощением в .NET рефакторинга «Введение внешнего метода» из Книги Мартина Фаулера (вплоть до сигнатуры метода). Они имеют в основном те же преимущества и недостатки. В разделе об этом рефакторинге он говорит, что это обходной путь, когда вы не можете изменить класс, который действительно должен владеть методом.
источник
В основном я рассматриваю методы расширения как признание того, что, возможно, им не следовало запрещать бесплатные функции.
В сообществе C ++ часто считается хорошей практикой ООП отдавать предпочтение свободным функциям, не являющимся членами, а не членам, потому что эти функции не нарушают инкапсуляцию, получая доступ к частным членам, которые им не нужны. Методы расширения кажутся окольными путями к тому же. То есть более чистый синтаксис для статических функций, у которых нет доступа к закрытым членам.
Методы расширения - это не что иное, как синтаксический сахар, но я не вижу в их использовании никакого вреда.
источник
источник
Я использую их для повторного использования классов объектной модели. У меня есть несколько классов, которые представляют объекты, которые у меня есть в базе данных. Эти классы используются на стороне клиента только для отображения объектов, поэтому основное использование - доступ к свойствам.
public class Stock { public Code { get; private set; } public Name { get; private set; } }
Из-за этого шаблона использования я не хочу, чтобы в этих классах были методы бизнес-логики, поэтому я делаю каждую бизнес-логику методом расширения.
public static class StockExtender { public static List <Quote> GetQuotesByDate(this Stock s, DateTime date) {...} }
Таким образом, я могу использовать одни и те же классы для обработки бизнес-логики и для отображения пользовательского интерфейса, не перегружая клиентскую часть ненужным кодом.
Одна интересная особенность этого решения заключается в том, что классы моей объектной модели динамически генерируются с использованием Mono.Cecil , поэтому было бы очень сложно добавить методы бизнес-логики, даже если бы я захотел. У меня есть компилятор, который читает файлы определения XML и генерирует эти классы-заглушки, представляющие какой-то объект, который у меня есть в базе данных. Единственный выход в этом случае - их продлить.
источник
Это позволяет C # лучше поддерживать динамические языки, LINQ и множество других вещей. Ознакомьтесь со статьей Скотта Гатри .
источник
В моем последнем проекте я использовал метод расширения для присоединения методов Validate () к бизнес-объектам. Я обосновал это тем, что бизнес-объекты, в которых используются сериализуемые объекты передачи данных, будут использоваться в разных доменах, поскольку они содержат общие объекты электронной коммерции, такие как продукт, покупатель, продавец и т. Д. Что ж, в разных доменах бизнес-правила могут быть разными, поэтому я инкапсулировал свои логика проверки с поздним связыванием в методе Validate, привязанном к базовому классу моих объектов передачи данных. Надеюсь, это имеет смысл :)
источник
Один случай, когда методы расширения были весьма полезны, - это клиентское приложение, использующее веб-службы ASMX. Из-за сериализации возвращаемые типы веб-методов не содержат никаких методов (на клиенте доступны только общедоступные свойства этих типов).
Методы расширения позволяли использовать для добавления функциональности (на стороне клиента) к типам, возвращаемым веб-методами, без необходимости создавать еще одну объектную модель или многочисленные классы-оболочки на стороне клиента.
источник
Методы расширения можно использовать для создания своего рода миксина в C #.
Это, в свою очередь, обеспечивает лучшее разделение проблем ортогональных концепций. Взгляните на этот ответ в качестве примера.
Это также можно использовать для включения ролей в C # - концепции, лежащей в основе архитектуры DCI .
источник
Также помните, что методы расширения были добавлены как способ сделать запрос Linq более читабельным при использовании в их стиле C #.
Эти две модификации абсолютно эквивалентны, но первая гораздо более читабельна (и разрыв в читаемости, конечно, увеличится с увеличением количества связанных методов).
int n1 = new List<int> {1,2,3}.Where(i => i % 2 != 0).Last(); int n2 = Enumerable.Last(Enumerable.Where(new List<int> {1,2,3}, i => i % 2 != 0));
Обратите внимание, что полный синтаксис должен быть даже таким:
int n1 = new List<int> {1,2,3}.Where<int>(i => i % 2 != 0).Last<int>(); int n2 = Enumerable.Last<int>(Enumerable.Where<int>(new List<int> {1,2,3}, i => i % 2 != 0));
Случайно, параметры типа
Where
иLast
не нуждаются в явном упоминании, поскольку они могут быть переданы благодаря наличию первого параметра этих двух методов (параметр, который вводится ключевым словомthis
и делает их методами расширения).Этот момент, очевидно, является преимуществом (среди прочего) методов расширения, и вы можете воспользоваться им в любом подобном сценарии, где задействовано объединение методов.
В частности, это более элегантный и убедительный способ, которым я нашел метод базового класса, вызываемый любым подклассом и возвращающий строго типизированную ссылку на этот подкласс (с типом подкласса).
Пример (хорошо, этот сценарий совершенно дрянной): после спокойной ночи животное открывает глаза, а затем кричит; каждое животное открывает глаза одинаково, тогда как собака лает, а утка квакает.
public abstract class Animal { //some code common to all animals } public static class AnimalExtension { public static TAnimal OpenTheEyes<TAnimal>(this TAnimal animal) where TAnimal : Animal { //Some code to flutter one's eyelashes and then open wide return animal; //returning a self reference to allow method chaining } } public class Dog : Animal { public void Bark() { /* ... */ } } public class Duck : Animal { public void Kwak() { /* ... */ } } class Program { static void Main(string[] args) { Dog Goofy = new Dog(); Duck Donald = new Duck(); Goofy.OpenTheEyes().Bark(); //*1 Donald.OpenTheEyes().Kwak(); //*2 } }
Концептуально
OpenTheEyes
должен бытьAnimal
метод, но он затем возвращает экземпляр абстрактного классаAnimal
, который не знает конкретных методов подкласса , какBark
илиDuck
или любой другой . Две строки, отмеченные как * 1 и * 2, вызовут ошибку компиляции.Но благодаря методам расширения у нас может быть своего рода «базовый метод, который знает тип подкласса, для которого он вызывается».
Обратите внимание, что простой универсальный метод мог бы выполнить эту работу, но гораздо более неудобным способом:
public abstract class Animal { //some code common to all animals public TAnimal OpenTheEyes<TAnimal>() where TAnimal : Animal { //Some code to flutter one's eyelashes and then open wide return (TAnimal)this; //returning a self reference to allow method chaining } }
На этот раз без параметра и, следовательно, без возможного вывода типа возвращаемого значения. Вызов не может быть ничем иным, как:
... который может сильно повесить код, если задействовано больше цепочек (особенно зная, что параметр типа всегда будет
<Dog>
в строке Гуфи и в строке<Duck>
Дональда ...)источник
Я могу сказать об этом только одно слово: ПОДДЕРЖИВАЕМОСТЬ это ключ к использованию методов расширения.
источник
Я думаю, что методы расширения помогают писать более понятный код.
Вместо того, чтобы помещать новый метод в свой класс, как предложил ваш друг, вы помещаете его в пространство имен ExtensionMethods. Таким образом вы сохраняете логический порядок в своем классе. Методы, которые на самом деле не имеют прямого отношения к вашему классу, не будут загромождать его.
Я чувствую, что методы расширения делают ваш код более понятным и организованным.
источник
Это позволяет вашему редактору / IDE делать умные предложения автозаполнения.
источник
Я люблю их за создание html. Часто есть разделы, которые используются многократно или генерируются рекурсивно, когда функция полезна, но в противном случае нарушила бы выполнение программы.
HTML_Out.Append("<ul>"); foreach (var i in items) if (i.Description != "") { HTML_Out.Append("<li>") .AppendAnchor(new string[]{ urlRoot, i.Description_Norm }, i.Description) .Append("<div>") .AppendImage(iconDir, i.Icon, i.Description) .Append(i.Categories.ToHTML(i.Description_Norm, urlRoot)).Append("</div></li>"); } return HTML_Out.Append("</ul>").ToString();
Существуют также ситуации, когда объекту требуется настраиваемая логика для подготовки к выходу HTML - методы расширения позволяют добавлять эту функциональность без смешивания представления и логики внутри класса.
источник
Я обнаружил, что методы расширения полезны для сопоставления вложенных универсальных аргументов.
Это звучит немного странно, но предположим, что у нас есть общий класс
MyGenericClass<TList>
, и мы знаем, что сам TList является универсальным (например,List<T>
), я не думаю, что есть способ найти этот вложенный 'T' из списка без какого-либо расширения методы или статические вспомогательные методы. Если в нашем распоряжении есть только статические вспомогательные методы, это (а) некрасиво и (б) заставит нас переместить функциональность, принадлежащую классу, во внешнее расположение.например, чтобы получить типы в кортеже и преобразовать их в сигнатуру метода, мы можем использовать методы расширения:
public class Tuple { } public class Tuple<T0> : Tuple { } public class Tuple<T0, T1> : Tuple<T0> { } public class Caller<TTuple> where TTuple : Tuple { /* ... */ } public static class CallerExtensions { public static void Call<T0>(this Caller<Tuple<T0>> caller, T0 p0) { /* ... */ } public static void Call<T0, T1>(this Caller<Tuple<T0, T1>> caller, T0 p0, T1 p1) { /* ... */ } } new Caller<Tuple<int>>().Call(10); new Caller<Tuple<string, int>>().Call("Hello", 10);
Тем не менее, я не уверен, где должна быть разделительная линия - когда метод должен быть методом расширения, а когда - статическим вспомогательным методом? Есть предположения?
источник
У меня есть зоны ввода на моем экране, и все они должны реализовывать стандартное поведение независимо от их точных типов (текстовые поля, флажки и т. Д.). Они не могут наследовать общий базовый класс, поскольку каждый тип зоны ввода уже является производным от определенного класса (TextInputBox и т. Д.)
Возможно, поднявшись в иерархии наследования, я смогу найти общего предка, например, WebControl, но я не разрабатывал класс фреймворка WebControl, и он не раскрывает то, что мне нужно.
С помощью метода расширения я могу:
1) расширить класс WebControl, а затем получить мое унифицированное стандартное поведение для всех моих входных классов
2) в качестве альтернативы сделать все мои классы производными от интерфейса, скажем, IInputZone, и расширить этот интерфейс с помощью методов. Теперь я могу вызывать методы расширений, связанные с интерфейсом, во всех моих зонах ввода. Таким образом, я добился своего рода множественного наследования, поскольку мои входные зоны уже получены из нескольких базовых классов.
источник
Есть так много отличных примеров методов расширения ... особенно для IEnumerables, как указано выше.
например, если у меня есть
IEnumerable<myObject>
метод создания и расширения дляIEnumerable<myObject>
... создать список
Без методов расширения пришлось бы вызывать:
myDisplayMethod(myOldArray); // can create more nexted brackets.
еще один отличный пример - мгновенное создание кругового связного списка!
Я могу поверить в это!
круговой связанный список с использованием методов расширения
Теперь объедините их, и код с использованием методов расширения выглядит следующим образом.
скорее, чем
с использованием методов расширения. Он более понятен, легче читается и больше ориентирован на объекты. также очень близко к:
Покажи это своему коллеге! :)
источник