Чтение онлайн

ЖАНРЫ

ЯЗЫК ПРОГРАММИРОВАНИЯ С# 2005 И ПЛАТФОРМА .NET 2.0. 3-е издание

Троелсен Эндрю

Шрифт:

 // и возвращающий целое значение.

 public delegate int BinaryOp(int x, int y);

 // Этот класс содержит методы, на которые

 // будет указывать BinaryOp.

 public class SimpleMath {

public static int Add(int x, int y) {return x + y;}

public static int Subtract(int x, int y) {return x – у;}

 }

 class Program {

static void Main(string[] args) {

Console.WriteLine("*****
пример делегата *****\n");

// Создание объекта BinaryOp,

// "указывающего" на SimpleMath.Add.

BinaryOp b = new BinaryOp(SimpleMath.Add);

// Вызов метода Add с помощью делегата.

Console.WriteLine(''10 + 10 равно {0}", b(10, 10));

Console.ReadLine;

}

 }

}

Снова обратите внимание на формат делегата BinaryOp, который может указывать на любой метод, принимающий два целых значения и возвращающий целое значение. Итак, мы создали класс с именем SimpleMath, определяющий два статических метода, которые (как неожиданно) соответствуют шаблону, определенному делегатом BinaryOp.

Чтобы добавить целевой метод в делегат, нужно просто передать имя этою метода конструктору делегата. Тогда вы сможете вызвать указанный член с помощью синтаксической конструкции, подобной прямому вызову функции.

// Здесь на самом деле вызывается Invoke!

Console.WriteLine("10 + 10 is {0}", b(10, 10));

"За кулисами" среда выполнения вызывает сгенерированный компилятором метод Invoke. Вы можете проверить это сами, если откроете компоновочный блок с помощью ildasm.exe и посмотрите на программный код CIL метода Main.

.method private hidebysig static void Main(string[] args) cil managed {

 …

 .locals init ([0] class SimpleDelegate.BinaryOp b)

 ldftn int32 SimpleDelegate.SimpleMath::Add(int32, int32)

 …

 newobj instance void SimpleDelegate.BinaryOp::.ctor(object, native int)

 stloc.0

 ldstr "10 + 10 is {0}"

 ldloc.0

 ldc.i4.s 10

 ldc.i4.s 10

 callvirt instance int32 SimpleDelegate.BinaryOp::Invoke(int32, int32)

 …

}

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

public class SimpleMath {

 public static int SquareNumber(int a) { return a * a; }

}

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

//
Ошибка! Метод не соответствует шаблону делегата!

BinaryOp b = new BinaryOp(SimpleMath.SquareNumber);

Исследование объекта делегата

Добавим в имеющийся пример вспомогательную функцию с именем DisplayDelegateInfo. Она будет выводить имена методов, поддерживаемых поступающим типом, производным от System.Delegate, а также имя класса, определяющего метод. Для этого мы выполним цикл по элементам массива System.Delegate, возвращенного из GetInvocationList, вызывая свойства Target и Method для каждого объекта.

static void DisplayDelegateInfo(Delegate delObj) {

 // Вывод имен каждого из элементов

 // списка вызовов делегата.

 foreach (Delegate d in delQbj.GetInvocationList) {

Console.WriteLine("Имя метода: {0}", d.Method);

Console.WriteLine("Имя типа: {0}", d.Target);

 }

}

Если изменить метод Main так, чтобы он вызывал этот новый вспомогательный метод, то вы увидите вывод, показанный на рис. 8.3.

Рис. 8.3. Проверка списка вызовов делегата

Обратите внимание на то, что здесь имя типа (SimpleMath) свойством Target не отображается. Причина в том, что наш делегат BinaryOp указывает на статические методы, следовательно, нет объекта, на который нужно ссылаться! Но если изменить методы Add и Subtract так, чтобы они перестали быть статическими, можно создать экземпляр типа SimpleMath и указать методы для вызова так, как показано ниже.

static void Main(string[] args) {

 Console.WriteLine("***** Пример делегата *****\n");

 // Делегаты .NET могут указывать на методы экземпляра.

 SimpleMath m = new SimpleMath;

 BinaryOp b = new BinaryOp(m.Add);

 // Вывод информации об объекте.

 DisplayDelegateInfо(b);

 Console.WriteLine("\n10 + 10 равно {0}", b(10, 10));

 Console.ReadLine;

}

Теперь вы должны увидеть вывод, показанный на рис. 8.4.

Рис. 8.4. Проверка списка вызовов делегата (новая попытка)

Исходный код. Проект SimpleDelegate размещен в подкаталоге, соответствующем главе 8.

Модификация типа Car с учетом делегатов

Очевидно, что предыдущий пример SimpleDelegate был исключительно иллюстративным, поскольку нет никаких реальных причин строить делегаты для простого сложения двух чисел. Но этот пример раскрывает принципы работы с типами делегата. Для построения более реального примера мы модифицируем тип Car так, чтобы он посылал сообщения Exploded и AboutToBlow через делегаты .NET, a не через пользовательский интерфейс обратного вызова. Кроме отказа от реализации IEngineEvents, мы должны выполнить следующие шаги:

Поделиться с друзьями: