CProgramming

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

Понятие о статических методах

Прежде чем переходить к методам расширения, необходимо несколько слов сказать о статических методах.

При объявлении статического метода используется ключевое слово static. Оно сообщает .NET, что этот метод работает не на конкретном экземпляре, а прикрепляется к классу в целом. Будучи статическими, эти методы не имеют доступа к состоянию какого-либо конкретного экземпляра, если он не передаётся методу в качества параметра (более подробно об этом дальше).

Статический метод Integer.Parse довольно хорошо известен по String.IsNullOrEmpty.

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

Теперь для получения наших книг делаем вот что:

var books = Books.GetBooks();

Довольно просто.

Методы расширения

Вернёмся к методам расширения.

Если коротко, то методы расширения — это особым образом объявляемые статические методы, которые компилятор позволяет вызывать на объектах с соответствующими сигнатурами. Объясню, что я имею в виду. Допустим, у нас есть вот такой статический метод:

Здесь мы можем принять любой экземпляр Book и передать его в Books.IsBoring, получив ответ с логическим значением. Изменим его, чтобы получился метод расширения.

Так как методы расширения могут быть объявлены только в статических классах (классах, которые не могут быть инстанцированы и имеют только статические члены), нам надо добавить к классу ключевое слово static.

Теперь объявляем наш метод IsBoring методом расширения, добавляя к первому параметру ключевое слово this вот таким образом:

Ключевое слово this сообщает платформе .NET, что IsBoring — это метод расширения, который может вызываться через синтаксис статического метода Books.IsBoring(someBook) или через синтаксис метода расширения someBook.IsBoring().

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

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

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

Ну вот, с методами расширения разобрались. Теперь узнаем, как их использовать для создания элегантного и плавного синтаксиса или предметно-ориентированного языка.

Цепочка методов расширения

Допустим, вам надо сделать книгу и выполнить ряд действий, чтобы книга получилась валидной. При этом вы хотели бы гибкий и читаемый API. Используйте методы расширения для создания предметно-ориентированного языка (DSL) в миниатюре.

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

Сколько всего здесь происходит! Но на самом деле, не так много, как кажется на первый взгляд.

Начнём с вызова Books.CreateBook. Это обращение к статическому методу, которое содержит строку с названием книги и возвращает некий таинственный объект.

Назовём этот объект BookBuilder, и выглядит он вот так:

Надеюсь, уже стало понятнее. И наш статический метод CreateBook теперь имеет такой вид:

В следующем примере мы вызываем метод WrittenBy. Вообще-то класс BookBuilder его не определяет. И можно было бы просто добавить этот метод в BookBuilder, но тогда методы расширения здесь были бы не нужны. Поэтому пусть класс BookBuilder определён каким-то кодом, который мы не контролируем и не можем изменить.

Добавим к WrittenBy метод расширения:

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

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

Во-вторых, метод вызывается только с одним заданным параметром, например WrittenBy("Michael Crichton"), потому что первый параметр выводится с использованием BookBuilder, на котором вызывается расширенный метод.

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

Может, и не самый изящный код в вашей жизни, но посмотрите, какой мощный и красивый синтаксис он может создавать!

Поговорим о LINQ

Методы расширения добавлялись в C#, очевидно, для поддержки языка интегрированных запросов LINQ в платформе .NET Framework 3.5.

Язык LINQ — одна из моих любимых функциональных возможностей C#, способствующих высокой производительности программиста, и методы расширения играют в нём важную роль.

Что можно делать благодаря LINQ?

Возможно, этот пример покажется несколько глупым, но всё это работает, если взять методы расширения, в которых есть IEnumerable<T> или IQueryable<T>, и использовать разные сигнатуры Func для фильтрации, сортировки или преобразования коллекции.

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

Заключение

Надеюсь, нам удалось приподнять завесу тайны над методами расширения, LINQ, статическими методами и методами экземпляра.

Уверен, что методы расширения (и LINQ в целом) — это один из ключевых факторов увеличения производительности, которые появились благодаря технологиям .NET, наряду с библиотекой базовых классов, общеязыковой средой выполнения CLR, Visual Studio и дженериками.

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

Читайте также:


Перевод статьи Matt Eland: Using Extension Methods in C# to Build Fluent Code