Добавлен , опубликован
Раздел:
Если вы находитесь на стадии обучения языку 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. Это не обязывает вас делать тоже самое, но насколько я видел в проектах всегда есть отдельная папочка для методов расширений с вот такими классами.
Вот наверное и вся основная информация по методам расширений. Спасибо за внимание.
`
ОЖИДАНИЕ РЕКЛАМЫ...

Показан только небольшой набор комментариев вокруг указанного. Перейти к актуальным.
29
bea_mind, я думаю, если еще и юзать nullable тип для возвращения, новичкам будет не легко
2
alexprey, никаких nullable здесь быть и не должно. По-моему правильный вариант - если метод будет генерировать исключения и/или иметь вид
public static bool GetLast(this int[] array, out int value).
27
bea_mind:
public static bool GetLast(this int[] array, out int value)
Нет уверенности что потенциальный читатель уже знает про out-параметры и про исключения. Откуда такое стремление усложнить подачу материала в целях того чтобы профи устроила правильность? Статья рассчитана на новичка, если освещаются методы расширений, то не надо приплетать еще 30 понятий. В этом и та беда почему по книгам сложно обучаться с нуля - они тут же начинают говорить про кучи, исключения, потокобезопасность и еще 40 не разъясненных понятий, при том не объясняя нифига, а потом думают что доступно изложили материал.
При условии, что в статье предупреждается, что мы экспериментируем не с самым правильным примером кода я думаю это допустимая погрешность.
29
bea_mind, имхо, с наллеблом было бы намного лучше, ну а вообще, да, согласен с экстравертом, зачем все усложнять?
Этот комментарий удален
20
Откуда такое стремление
Переучивать сложнее, чем учить с нуля.
Это я не критикую статью, просто аргумент.
Этот комментарий удален
29
nvc123, тоесть предлагаешь бедному новичку сразу дать не объясняя овер дофига тем, чтобы он тупо это все скопипастил не понимая?
Этот комментарий удален
Показан только небольшой набор комментариев вокруг указанного. Перейти к актуальным.
Чтобы оставить комментарий, пожалуйста, войдите на сайт.