English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Основной курс C#

Дополнительный курс C#

C# ООП (Объектно-ориентированное программирование)

Делегаты (Delegate) в C#

Если мы хотим передавать функцию в качестве параметра, что делать? Как C# обрабатывает обратные вызовы или обработчики событий? Ответ - делегат (Delegate). Делегат (Delegate) - это тип переменной, которая хранит ссылку на метод. Ссылка может быть изменена в режиме выполнения.

Делегат являетсяопределение подписи методатип данных по ссылке. Вы можете определить переменную делегата, как и другие типы данных, которые могут ссылаться на любой метод с подписью, совпадающей с подписью делегата.

Работа с делегатами включает три шага:

  1. Объявление делегата

  2. Установка целевого метода

  3. Вызов делегата

Делегат можно объявить с помощью ключевого слова delegate и подписи функции, как показано ниже.

  Синтаксис делегата

[доступ] delegate [возвращаемый тип] [имя делегата]([параметры])

Ниже приведена декларация делегата под названием MyDelegate.

public delegate void MyDelegate(string msg);

Ниже мы объявили MyDelegate сvoidВозвращаемый тип и строковый параметр. Делегат можно объявить как внутри, так и вне класса. Фактически, его лучше объявлять вне класса.

После объявления делегата нам нужно установить целевой метод или lambda-выражение. Это можно сделать, создав объект делегата с помощью ключевого слова new и передав метод, подпись которого соответствует подписи делегата.

public delegate void MyDelegate(string msg); // декларация делегата
// установление целевого метода
MyDelegate del = new MyDelegate(MethodA);
// или
MyDelegate del = MethodA; 
// или lambda-выражение 
MyDelegate del = (string msg) => Console.WriteLine(msg);
// целевой метод
static void MethodA(string message)
{
    Console.WriteLine(message);
{}

Целевой метод можно установить напрямую, не создавая объекта делегата, например, MyDelegate del = MethodA.

После установки целевого метода можно использовать метод Invoke() или использовать оператор () для вызова делегата.

del.Invoke("Hello World!");
// или 
del("Hello World!");

Ниже приведен полный пример делегата.

public delegate void MyDelegate(string msg); // Объявление делегата
class Program
{
    static void Main(string[] args)
    {
        MyDelegate del = ClassA.MethodA;
        del("Hello World");
        del = ClassB.MethodB;
        del("Hello World");
        del = (string msg) => Console.WriteLine("Called lambda expression: " + msg);
        del("Hello World");
    {}
{}
class ClassA
{
    static void MethodA(string message)
    {
        Console.WriteLine("Called ClassA.MethodA() with parameter: " + message);
    {}
{}
class ClassB
{
    static void MethodB(string message)
    {
        Console.WriteLine("Called ClassB.MethodB() with parameter: " + message);
    {}
{}

На следующем рисунке показан делегат.

Делегат C#

Передача делегата в качестве параметра

Метод может иметь параметр типа делегата, как показано ниже.

public delegate void MyDelegate(string msg); // Объявление делегата
class Program
{
    static void Main(string[] args)
    {
        MyDelegate del = ClassA.MethodA;
        InvokeDelegate(del);
        del = ClassB.MethodB;
        InvokeDelegate(del);
        del = (string msg) => Console.WriteLine("Called lambda expression: " + msg);
        InvokeDelegate(del);
    {}
    static void InvokeDelegate(MyDelegate del) // Параметр типа MyDelegate
    {
        del("Hello World");
    {}
{}
class ClassA
{
    static void MethodA(string message)
    {
        Console.WriteLine("Called ClassA.MethodA() with parameter: " + message);
    {}
{}
class ClassB
{
    static void MethodB(string message)
    {
        Console.WriteLine("Called ClassB.MethodB() with parameter: " + message);
    {}
{}

В .NET типы Func и Action являются вbuilt-генерическими делегатами и должны использоваться для наиболее распространённых делегатов, а не для создания новых пользовательских делегатов.

Multicast delegate

Делегаты могут указывать на несколько методов. Делегаты, указывающие на несколько методов, называются multicast delegate. Операторы '+' или '+=' добавляют функцию в список вызовов, а операторы '-' и '-=' удаляют её.

public delegate void MyDelegate(string msg); // Объявление делегата
class Program
{
    static void Main(string[] args)
    {
        MyDelegate del1 = ClassA.MethodA;
        MyDelegate del2 = ClassB.MethodB;
        MyDelegate del = del1 + del2; // del1 + del2
        del("Hello World");
        MyDelegate del3 = (string msg) => Console.WriteLine("Called lambda expression: " + msg);
        del += del3; // del1 + del2 + del3
        del("Hello World");
        del = del - del2; // Удалить del2
        del("Hello World");
        del -= del1 // Удалить del1
        del("Hello World");
    {}
{}
class ClassA
{
    static void MethodA(string message)
    {
        Console.WriteLine("Called ClassA.MethodA() with parameter: " + message);
    {}
{}
class ClassB
{
    static void MethodB(string message)
    {
        Console.WriteLine("Called ClassB.MethodB() with parameter: " + message);
    {}
{}

Операторы сложения и вычитания всегда работают как часть присваивания: del1 += del2; это эквивалентно del1 = del1 + del2; также и с вычитанием.

Если делегат возвращает значение, то при вызове multicast делегата возвращается значение последнего распределенного метода.

public delegate int MyDelegate(); // объявление делегата
class Program
{
    static void Main(string[] args)
    {
        MyDelegate del1 = ClassA.MethodA;
        MyDelegate del2 = ClassB.MethodB;
        MyDelegate del = del1 + del2; 
        Console.WriteLine(del());// возвращается 200
    {}
{}
class ClassA
{
    static int MethodA()
    {
        return 100;
    {}
{}
class ClassB
{
    static int MethodB()
    {
        return 200;
    {}
{}

Генерические делегаты

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

Например, посмотрите на общие делегаты для параметров int и string.

public delegate T add<T>(T param1, T param2); // генерический делегат
class Program
{
    static void Main(string[] args)
    {
        add<int> sum = Sum;
        Console.WriteLine(sum(10, 20));
        add<string> con = Concat;
        Console.WriteLine(conct("Hello", "World!!"));
    {}
    public static int Sum(int val1, int val2)
    {
        return val1 + val2;
    {}
    public static string Concat(string str1, string str2)
    {
        return str1 + str2;
    {}
{}

Делегаты также используются для объявления событий и анонимных методов.

 Важно помнить:

  1. Делегат - это тип данных, представляющий собой ссылку на подпись.

  2. Переменная типа делегата может ссылаться на любое метод, имеющее такую же подпись, как и делегат.

  3. Грамматика:[Доступ修饰итель] делегат [возвратный тип] [имя делегата]([параметры])([access modifier] delegate [return type] [delegate name]([parameters]))

  4. Подпись метода цели должно соответствовать подписи делегата.

  5. Делегаты могут быть вызваны так же, как и обычные функции или invoke() метод.

  6. Множественные методы могут быть добавлены к делегату с помощью операторов '+' или '+=', и удалены с помощью операторов '-' или '-=', что называется многопоточными делегатами.

  7. Если многопоточность делегата возвращает значение, то оно возвращается из последнего назначения метода.

  8. Делегаты используются для объявления событий и анонимных методов в C#.