Шаг 175.
Язык программирования C#. Начала.
Делегаты и события. Использование анонимных методов

    На этом шаге мы рассмотрим пример использования анонимных методов.

    Мы рассмотрим несколько примеров, в которых используются анонимные методы. Примеры призваны послужить иллюстрацией к тому, где, когда и как анонимные методы могут оказаться полезными.

    На предыдущих шагах упоминалось, что анонимные методы предназначены для присваивания в качестве значений переменным типа делегата. Но это вовсе не означает, что мы не можем передать анонимный метод аргументом другому методу или даже конструктору (правда тут имеются некоторые особенности). Противоречия здесь нет, поскольку соответствующий аргумент описывается как переменная типа делегата, и когда фактическим аргументом передается анонимный метод, то технически переменной типа делегата значением присваивается анонимный метод. То есть все формальности будут выдержаны.

    Сначала рассмотрим очень простой пример, в котором имеется делегат MyDelegate, соответствующий методам с одним целочисленным аргументом и символьным результатом. В программе описывается класс MyClass, у которого есть открытое символьное поле symbol, конструктор с двумя аргументами и метод set(). Также в классе описано поле get, которое является ссылкой на экземпляр делегата MyDelegate. Первым аргументом конструктору передается символьное значение, присваиваемое полю symbol. Второй аргумент конструктора является ссылкой на экземпляр делегата MyDelegate. Эта ссылка записывается в поле get. Программный код представлен в примере ниже.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace pr175_1
{
    // Объявление делегата: 
    delegate char MyDelegate(int n);
    
    // Класс: 
    class MyClass {
        // Открытое символьное поле: 
        public char symbol;
        // Поле, являющееся ссылкой на экземпляр делегата: 
        public MyDelegate get;
        // Конструктор, второй аргумент которого ссылка на
        // экземпляр делегата:
        public MyClass(char s, MyDelegate md) {
            // Присваивание значения полю: 
            symbol = s;
            // Использование ссылки на экземпляр делегата: 
            get = md;
        }
        // Метод с аргументом, являющимся ссылкой на экземпляр 
        // делегата:
        public void set(MyDelegate md) {
            // Использование ссылки на экземпляр делегата: 
            get = md;
        }
    }

    // Класс с главным методом: 
    class Program
    {
        // Главный метод:
        static void Main()
        {
            // Создание объекта. Вторым аргументом является 
            // анонимный метод:
            MyClass obj = new MyClass('K', // Символьный аргумент 
                delegate(int n)
                {          // Анонимный метод
                    return (char)('A' + n);
                }
        );
            // Вызов экземпляра делегата:
            Console.WriteLine("Символ: \'{0}\'", obj.get(3));
            // Вызов метода, аргументом которому передан
            // анонимный метод:
            obj.set(
                delegate(int n)
                {
                    return (char)(obj.symbol + n);
                }
            );
            // Вызов экземпляра делегата:
            Console.WriteLine("Символ: \'{0}\'", obj.get(3));
            // Задержка:
            Console.ReadLine();
        }
    }
}
Архив проекта можно взять здесь.

    Ниже представлен результат выполнения программы:


Рис.1. Результат выполнения программы

    Для нас первоочередной интерес представляет код главного метода программы. В нем создается объект obj класса MyClass. Причем второй аргумент конструктора описан довольно громоздким выражением, которое является описанием анонимного метода:

  delegate(int n)
                {          // Анонимный метод
                    return (char)('A' + n);
                }

    Вся эта конструкция является вторым аргументом конструктора. Это анонимный метод, который при вызове с целочисленным аргументом n результатом возвращает символ, смещенный по отношению к символу 'A' на n позиций. Ссылка на этот анонимный метод записывается через посредника (экземпляр делегата) в поле get. Поэтому при вызове данного экземпляра делегата командой obj.get(3) в качестве результата возвращается символ, отстоящий в кодовой таблице на 3 позиции (в направлении увеличения кода) по отношению к символу 'A'.


То есть в данном случае поле symbol объекта при определении анонимного метода не используется. Это, в общем-то, не случайно. При создании объекта obj, когда вызывается конструктор, ссылка obj еще не может использоваться. Поэтому в теле анонимного метода ссылок на объект obj нет.

    Далее из объекта obj вызывается метод set(), которому в качестве аргумента передается следующая конструкция:

  delegate(int n)
                {
                    return (char)(obj.symbol + n);
                }

    Это анонимный метод, который присваивается в качестве значения полю get объекта obj. Результатом метод возвращает символ, смещенный на n (аргумент метода) позиций по отношению к символьному значению поля symbol объекта obj. При создании объекта obj поле symbol получило значение 'K'. Поэтому значением выражения obj.get(3) будет символ, смещенный на 3 позиции по отношению к символу 'K'.


При вызове метода set() мы использовали анонимный метод, содержащий ссылку obj.symbol. Здесь нет ошибки, поскольку на момент вызова метода set() в переменную obj уже была записана ссылка на объект. В конструкторе, при создании объекта obj, мы не могли использовать ссылку obj.symbol. Причина очевидна: сначала вычисляются аргументы конструктора, а уже затем выполняется его код. Ссылка на объект записывается в переменную obj после того, как объект создан. Поэтому при вычислении аргументов конструктора переменная obj еще не содержит ссылку на объект.

    На следующем шаге мы продолжим изучение этого вопроса.




Предыдущий шаг Содержание Следующий шаг