Добавлен , опубликован
Раздел:
Если вы находитесь на стадии обучения языку C# - данная статья как раз для вас! Она расскажет вам о том, как использовать одну из синтаксических фич языка - методы расширений.
Итак, для чего нужны методы расширений?
Что, ж, я рассмотрю использование подобной конструкции на конкретном примере, правда не самом правильном - этот пример выбран специально и устроен так, чтобы не задевать другие темы программирования и не мешать в голове понятия.

Пример

Так случилось, что в вашем проекте постоянно приходится доставать последний элемент из массивов типа int.
Чтобы доставать последний элемент массива array вам приходится каждый раз писать код на подобии следующего:
var last = 0;
if (array != null && array.Count != 0) 
    last = array[array.Length - 1];
В результате в переменной last хранится либо последний элемент массива, либо, если массив оказался пустым - хранится 0, как значение по умолчанию.
Вы долго и муторно вбиваете этот код при каждом использовании, пока однажды к вам в голову не приходит идея - "а почему бы мне не выделить написанное в отдельный метод"?
И вы создаете метод на подобии вот такого:
public static class IntArrayUtils 
{
    public static int GetLast(int[] array) 
    {
        var last = 0;
        if (array != null && array.Length != 0)
             last = array[array.Length - 1];
        return last;
    }
}
Который вызывается в коде вот так:
var last = IntArrayUtils.GetLast(array);
Согласитесь, уже короче? Но чего -то не хватает. Может, красоты?
Вот тут мы и подошли к методам расширений. Согласитесь, что вот так было бы лучше:
var last = array.GetLast();
Нам бы не пришлось указывать странный метод IntArrayUtils и данный метод выглядел бы в точь-в-точь как родной метод у int[], который, к сожалению мы добавить не можем.

Решение

Что же нужно изменить чтобы достичь такого результата? Все очень просто, к существующему методу мы добавим всего одно слово this перед первым параметром:
public static class IntArrayUtils 
{
    public static int GetLast(this int[] array) 
    {
        var last = 0;
        if (array != null && array.Length != 0)
             last = array[array.Length - 1];
        return last;
    }
}
В результате все экземпляры int[] в нашем коде обзавелись дополнительным методом GetLast().
Ваш код после такой простой манипуляции может вызываться двумя разными способами, которые я уже указывал выше.
Как стандартным, через статический класс:
var last = IntArrayUtils.GetLast(array);
Так и через экземпляр:
var last = array.GetLast();

Заметки

Немного тонкостей о методах расширений:
  • Метод расширения обязан быть частью static класса
  • Несмотря на то, что метод выглядит как метод экземпляра, он является статичным и при компиляции подменяется. Это обязывает вас делать проверку на null, чтобы не допустить ошибок.
  • Метод может иметь собственные Generic типы, как и любой другой метод. Это позволит вам сделать код более универсальным.
  • Увы, вы не сможете расширить этим статические классы, например добавить новый статический метод в класс Math. Только экземпляры, только хардкор.

Примеры использования

Несмотря на все удобство, методы расширений не стоит применять без повода - чрезмерная перенасыщенность ваших классов может плохо сказываться на читаемости и поддерживании вашего кода. Потому настоятельно советую использовать данный "сахар" с умом.
Код желательно обустраивать так, чтобы его было легко читать. Например:
text.SaveAsFile(file);
Среди стандартных библиотек данный способ используется для всех методов у массивов и перечислителей - методы ForEach, Select, Any, Where, Contains и так далее.
Очевидно, что подобное решение отлично подходит для случаев, в которые просто так метод не добавишь, либо этот метод весьма специфичен и требует возможностей static классов. Например, к таким относится удаление объекта, где обычный метод экземпляра не может обнулить экземпляр, через this = null потому что сам является его частью.
Несмотря на плюсы, не стоит использовать методы расширений если:
  • Ваш метод лучше укладывается в отдельном классе с другими тематичными методами и ситуация использования крайне специфична или просто редка для этого типа в целом.
  • Ваш метод перекликается с названием существующих методов и порождает путаницу.
  • Ваш метод выглядит читабельней, не используя данного синтаксиса.
Общепринято складывать методы расширений в отдельный класс (без обычных статических методов) и дописывать данному классу слово Extensions. Например, класс, содержащий методы расширений для типа List обычно называют ExtensionsList или ListExtensions. Это не обязывает вас делать тоже самое, но насколько я видел в проектах всегда есть отдельная папочка для методов расширений с вот такими классами.
Вот наверное и вся основная информация по методам расширений. Спасибо за внимание.
`
ОЖИДАНИЕ РЕКЛАМЫ...

Показан только небольшой набор комментариев вокруг указанного. Перейти к актуальным.
24
Extravert, лично я шел в школу с твердой уверенностью что на ноль делить можно и это доставило мне в свое время немало проблем, не говоря уже об уверенности что если из единицы вычесть два, то получится не ноль, а минус единица.
К чему это я? Наверно к тому, что осваивать программирование, как правило, начинают все-же не в первом классе, а уже более зрелые личности, способные к мыслительной деятельности повыше уровнем чем "добавим пять слонов к девяти бананам". Да и синтаксический сахар вроде методов расширения это явно не первое, чему должен учиться человек, начинающий изучать программирование, а значит и статья на эту тему не обязательно должна быть написана в столь аскетичной манере.
Не говоря уже о том, что конкретно методы расширения есть зло, нарушающее принципы ООП и взрывающее мозг почитателям более-менее грамотной архитектуры приложения, а также поборникам чистоты, читаемости и самодостаточности кода. И да, вот еще что, имена методов с большой буквы - ааааа моооииии глаааазааааа!
P.S. так-то статья составлена достаточно грамотно, ничего личного.
29
имена методов с большой буквы - ааааа моооииии глаааазааааа!
вот только не надо тут холиваров по стилю написания кода.
prog, Mihahail, nvc123, bea_mind, вот вы все молодцы, хоть бы одну статью написали. Человек старается и пытается развить сайт, а вы тут сидите и пинаете палочки. Говорил, и еще раз скажу, хотите показать свою крутоту всем? Так возьмите и напишите статью
prog:
Не говоря уже о том, что конкретно методы расширения есть зло
не понимаешь всю их прелесть, они упрощают твою жизнь.
3 комментария удалено
20
Методы расширения было бы лучше реализовывать переопределяя класс array его потомком(ой, ну да, этож c#) с заимплеменченным методом .last()
Тогда это не выглядело бы как костыль, на мой скромный взгляд.
alexprey, я вообще мимокрокодил. Но аргумент "сперва добейся" - не аргумент.
Extravert, не воспринимай близко к сердцу. Статья-то хорошая, её хоть сейчас помещай в учебник.
Мы тут просто начали оффтопить на близкую, но другую тему: как лучше эту фичу использовать в больших серьезных проектах. В статье настолько всё разжевано и понятно, что обсуждать приходится что угодно, кроме статьи.
2
Нет уверенности что потенциальный читатель уже знает про out-параметры и про исключения.
Так и нету уверенности, что он знает static, public, class и.т.д.
В этом и та беда почему по книгам сложно обучаться с нуля - они тут же начинают говорить про кучи, исключения, потокобезопасность и еще 40 не разъясненных понятий, при том не объясняя нифига, а потом думают что доступно изложили материал.
Вот про книги не надо. В большинстве из них оговаривается, что конструкции языка тесно связаны, и на ненужные просто не обращать внимания.
Скорее всего вам не повезло с хорошей литературой.
Вообще подобные статьи должны быть нацелены на тех, кто не совсем или в тяжелом случае совершенно ничего не понял, что "это" такое.
А вид иметь примерно такой:
  • полное описание в доступной форме(зачем? когда? как?).
  • реальные примеры и побольше.
  • различные тонкости применения и "подводные камни", опять же с обильным количеством примеров.
27
bea_mind:
Так и нету уверенности, что он знает static, public, class и.т.д.
Есть разница между тем, что ему придется узнать чтобы это осилить, и тем что он может узнать потом. Все в программировании очень сильно переплетается, но нельзя одним махом срубить все сразу.
24
alexprey, C# вызывает у меня дикий когнитивный диссонанс, как можно было понять из оффтопа в прошлом комментарии. А полезное для сайта я делать пытался, пока время позволяло - переводил статьи по JmonkeyEngine3 и еще вернусь к этому, даже не смотря на полное отсутствие востребованности этой темы.
Что касается методов расширения, расскажу одну маленькую историю из своей жизни: было дело, работал я на C# и выпало мне поддерживать библиотеку, написанную задолго до моего прихода в проект. Так получилось, что эта библиотека понадобилась одной из групп разработчиков на смежном проекте.
И вот, приходят ко мне с вопросом "как пользоваться методом Revitalize для объекта типа LostRequest? Мы его и так и эдак, а ему хоть бы хны - фиолетовый" имена методов и классов, а также пароли и явки взяты произвольно из соображений анонимности. Я то помню что такого метода в нашей библиотеке нет и быть не может, потому в первую очередь спрашиваю не используют ли они какую-то самопальную модификацию, но нет, ничего такого.
Потом вспоминаю про методы расширения и мягко уточняю не пользуются ли они чем-то таким, но и тут все чисто - не писали, не употребляли, не привлекались. В итоге мне пришлось скачать и развернуть у себя их проект, весивший, к слову, пару гигов со всеми необходимыми составляющими. И все только для того, чтобы в другой библиотеке обнаружить этот-самый метод-расширение, расширявший класс из нашей библиотеки.
Мораль - реализация этого расширения через фасад, адаптер, агрегацию или любым другим применимым в данном случае архитектурным паттерном значительно упростила бы жизнь, как минимум, пяти программистам ценой лишних десяти минут работы одного программиста.
29
prog, что это за программисты такие которые не могут отследить где метод расположен.... Студия такие методы выделяет и отправляет в нужный класс. Просто им было лень и скинули дело на тебя. Проблема фасада в том, что если добавят новйы метод в основной класс, тебе придется расширять новый и т.д. У нас в проекте с большим успехом юзают расширения для простоты написания кода и никто не жалуется.
Mihahail:
alexprey, я вообще мимокрокодил. Но аргумент "сперва добейся" - не аргумент.
Моя цель сподвигнуть других писать статьи) А вообще экстраверт тоже, сперва пришел и начал везде всех расскидывать такими же контр-комментариями, но он хотя бы доказал что он может не только писать заумные комментарии.
Я лично помогал в том году нашему преподавателю разрабатывать учебник для первого курса по плюсам и шарпам. Там было все так же разжованно, а в некоторых местах еще лучше. Большинство все равно так и не понимали как это делать, пока им устно не расскажешь, но в итоге, они все таки освоили язык. Поэтому давай те не будем тут разводить срачи и напишем каждый по несколько статей по основом любого языка.
Вот ты prog, например, ты отлично знаешь джаву, так почему бы не научить других? Я уверен, у тебя бы получились отличные статьи, наверняка.
И на последок, почему то мне кажется вы не дочитываете статью, или просто пробежались по коду и все. Потому что там было сказано, что их их надо использовть с умом, а не тыкать везде и всюду... вот теперь точно все
Этот комментарий удален
Показан только небольшой набор комментариев вокруг указанного. Перейти к актуальным.
Чтобы оставить комментарий, пожалуйста, войдите на сайт.