воскресенье, 18 октября 2009 г.

.Net 3 - Сила методов расширения

Методы расширения (Extension Methods) это простой такой трюк компилятора, который позволяет программисту добавлять свои собственные методы к уже существующим классам.
Естесственно, чтобы не нарушить защиту класса, методам расширения недоступны “кишки” класса, к которому они пристраиваются. Метод расширение обязан пользоваться существующим публичным контрактом.
Фактически, это такой способ подстроить синтаксис уже существующей библиотеки/фреймворка “под себя”, уменьшив количество кода, которое нужно написать для реализации требуемой функциональности (любители Nemerle меня поймут :-)
Передо мной недавно встала следующая задача: во множестве пользовательских контролов ASP.NET приложения нужно было анализировать дерево контролов страницы и выцепить из него самый первый контрол, который реализовал определенный интерфейс.
Метод FindControl для этого не подходил – я не имел возможности задавать имя контрола. В результате были написаны пара методов-раширений, которые добавляли требуемую функциональность




/// <summary>
    /// returns all controls by wisiting control hierarchy
    /// </summary>
    public static IEnumerable<Control> AllControls(this Control control)
    {
        foreach (Control c in control.Controls)
        {
            yield return c; // build up control collection implicitly

            if (c.Controls.Count > 0)
            {
                foreach (Control cc in AllControls(c))
                    yield return cc; // build up control collection implicitly
            }
        }
    }

    /// <summary>
    /// returns all controls on page implementing a given interface
    /// </summary>
    public static T FirstControlOfType<T>(this Control control) where T : class
    {
        foreach (var cn in control.AllControls())
        {
            var c = cn as T;
            if (c != null)
                return c;
        }
        return null;
    }

Метод AllControls() возвращает коллекцию всех элементов в дереве Page Controls (конструкция yield return позволяет не строить вручную коллекцию через ListArray)
Метод FirstControlOfType() возвращает первый встретившийся в дереве контрол, реализующий требуемый интерфейс
Пользоваться расширениями – одно удовольствие:
private DateTime DateFilter
{
    get
    {
        var filtered = this.Page.FirstControlOfType<IFilteredByDate>();
        return filtered == null ? DateTime.Now: filtered.DateFilter;
    }
}

Чем методы расширения лучше статических методов? Тем, что во-первых не нужно помнить имена всевозможных ControlUtils, DateUtils, StringUtils, а во-вторых, при кодировании ты концентрируешься своей задаче, а не на хелперах, которые помогают задачу решать.
То есть “пляшешь” от реальных данных, не размазываешь свое внимания по финтифлюшечкам.

3 комментария:

Big 40wt Svetlyak комментирует...

Приятно, что .NET потихоньку движется в сторону нормальных языков программирования, типа Python :-D

Анонимный комментирует...

Да скорее питон движется к .net, вот только как-то медленно

Yury Skaletskiy комментирует...

а в питоне есть свой аналог extension methods?