Рейтинговые книги
Читем онлайн Язык программирования C#9 и платформа .NET5 - Эндрю Троелсен

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 151 152 153 154 155 156 157 158 159 ... 407
Delegate Remove(

    Delegate source, Delegate value);

  public static Delegate RemoveAll(

    Delegate source, Delegate value);

  // Перегруженные операции.

  public static bool operator ==(Delegate d1, Delegate d2);

  public static bool operator !=(Delegate d1, Delegate d2);

  // Свойства, открывающие доступ к цели делегата.

  public MethodInfo Method { get; }

  public object Target { get; }

}

Имейте в виду, что вы никогда не сможете напрямую наследовать от таких базовых классов в своем коде (попытка наследования приводит к ошибке на этапе компиляции). Тем не менее, когда вы используете ключевое слово delegate, то тем самым неявно создаете класс, который "является" MulticastDelegate. В табл. 12.1 описаны основные члены, общие для всех типов делегатов.

Пример простейшего делегата

На первый взгляд делегаты могут показаться несколько запутанными. Рассмотрим для начала простой проект консольного приложения (по имени SimpleDelegate), в котором применяется определенный ранее тип делегата BinaryOp. Ниже показан полный код с последующим анализом:

// SimpleMath.cs

namespace SimpleDelegate

{

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

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

  public class SimpleMath

  {

    public static int Add(int x, int y) => x + y;

    public static int Subtract(int x, int y) => x - y;

  }

}

// Program.cs

using System;

using SimpleDelegate;

Console.WriteLine("***** Simple Delegate Example *****n");

// Создать объект делегата BinaryOp, который

// "указывает" на SimpleMath.Add().

BinaryOp b = new BinaryOp(SimpleMath.Add);

// Вызвать метод Add() косвенно с использованием объекта делегата.

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

Console.ReadLine();

// Дополнительные определения типов должны находиться

// в конце операторов верхнего уровня.

// Этот делегат может указывать на любой метод,

// принимающий два целых числа и возвращающий целое число.

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

На заметку! Вспомните из главы 3, что дополнительные определения типов (делегат BinaryOp в этом примере) должны располагаться после всех операторов верхнего уровня.

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

Когда вы хотите присвоить целевой метод заданному объекту делегата, просто передайте имя нужного метода конструктору делегата:

// Создать объект делегата BinaryOp, который

// "указывает" на SimpleMath.Add().

BinaryOp b = new BinaryOp(SimpleMath.Add);

На данной стадии метод, на который указывает делегат, можно вызывать с использованием синтаксиса, выглядящего подобным прямому вызову функции:

// На самом деле здесь вызывается метод Invoke()!

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

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

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

{

  ...

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

}

Язык C# вовсе не требует явного вызова метода Invoke() внутри вашего кода. Поскольку BinaryOp может указывать на методы, которые принимают два аргумента, следующий оператор тоже допустим:

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

Вспомните, что делегаты .NET Core безопасны в отношении типов. Следовательно, если вы попытаетесь передать делегату метод, который не соответствует его шаблону, то получите ошибку на этапе компиляции. В целях иллюстрации предположим, что в классе SimpleMath теперь определен дополнительный метод по имени SquareNumber(), принимающий единственный целочисленный аргумент:

public class SimpleMath

{

  public static int SquareNumber(int a) => a * a;

}

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

// Ошибка на этапе компиляции! Метод не соответствует шаблону делегата!

BinaryOp b2 = new BinaryOp(SimpleMath.SquareNumber);

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

Давайте усложним текущий пример, создав в классе Program статический метод (по имени DisplayDelegatelnfо()). Он будет выводить на консоль имена методов, поддерживаемых объектом делегата, а также имя класса, определяющего метод. Для этого организуется итерация по массиву System.Delegate, возвращенному методом GetlnvocationList(), с обращением к свойствам Target и Method каждого объекта:

static void DisplayDelegateInfo(Delegate delObj)

{

  // Вывести имена всех членов в списке вызовов делегата.

  foreach (Delegate d in delObj.GetInvocationList())

  {

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

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

  }

}

Предполагая, что в метод Main() добавлен вызов нового вспомогательного метода:

BinaryOp b = new BinaryOp(SimpleMath.Add);

DisplayDelegateInfo(b);

вывод приложения будет таким:

***** Simple Delegate Example *****

Method Name: Int32 Add(Int32, Int32)

Type Name:

10 + 10 is 20

Обратите внимание, что при обращении к свойству Target имя целевого класса (SimpleMath) в настоящий момент не отображается. Причина в том, что делегат BinaryOp указывает на статический метод, и потому объект для ссылки попросту отсутствует! Однако если сделать методы Add() и Substract() нестатическими (удалив ключевое слово static из их объявлений), тогда можно будет создавать экземпляр класса SimpleMath и указывать методы для вызова с применением ссылки на объект:

using System;

using SimpleDelegate;

Console.WriteLine("***** Simple Delegate Example *****n");

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

SimpleMath m = new SimpleMath();

BinaryOp b = new BinaryOp(m.Add);

// Вывести сведения об объекте.

DisplayDelegateInfo(b);

1 ... 151 152 153 154 155 156 157 158 159 ... 407
На этой странице вы можете бесплатно читать книгу Язык программирования C#9 и платформа .NET5 - Эндрю Троелсен бесплатно.

Оставить комментарий