C# Делегат
В C# делегат — это типобезопасный указатель на функцию, который позволяет передавать методы в качестве параметров другим методам.
Делегаты в C# похожи на указатели на функции в C или C++.Делегат— это переменная ссылочного типа, которая хранит ссылку на метод; ссылку можно изменить во время выполнения.
Делегаты очень распространены в C# и используются для обработки событий, функций обратного вызова, LINQ и других операций.
Все делегаты являются производными от классаSystem.Delegate.
Объявление делегата
Делегат — это ссылочный тип, который определяет сигнатуру метода и может использоваться для хранения метода, соответствующего этой сигнатуре. С помощью делегата можно вызывать методы в других классах.
Объявление делегата определяет, на какие методы может ссылаться делегат. Делегат может указывать на метод с той же сигнатурой.
Синтаксис объявления делегата выглядит следующим образом:
public delegate <return type> <delegate-name> <parameter list>
Описание китайского формата:
public delegate 返回类型 委托名(参数类型 参数名, ...);
Например, следующий код определяет делегат, который принимает два целых числа и возвращает целое число:
public delegate int MathOperation(int x, int y);
Делегат в следующем примере может использоваться для ссылки на любой метод, который имеет одинstringметод с параметрами и возвращаетintпеременные типа.
public delegate int MyDelegate (string s);
Создание экземпляра делегата (Delegate)
После объявления типа делегата необходимо создать объект делегата с помощью ключевого словаnew, и он связывается с конкретным методом. При создании делегата аргумент, передаваемый вnew-инструкцию, записывается как вызов метода, но без параметров. Например:
...
printString ps1 = new printString(WriteToScreen);
printString ps2 = new printString(WriteToFile);
В следующем примере демонстрируется объявление, создание экземпляра и использование делегата, который может использоваться для ссылки на метод, принимающий целочисленный параметр и возвращающий целочисленное значение.
Пример
delegate int NumberChanger(int n);
namespace DelegateAppl
{
class TestDelegate
{
static int num = 10;
public static int AddNum(int p)
{
num += p;
return num;
}
public static int MultNum(int q)
{
num *= q;
return num;
}
public static int getNum()
{
return num;
}
static void Main(string[] args)
{
// Создать экземпляр делегата
NumberChanger nc1 = new NumberChanger(AddNum);
NumberChanger nc2 = new NumberChanger(MultNum);
// Использовать объект делегата для вызова метода
nc1(25);
Console.WriteLine("Значение Num: {0}", getNum());
nc2(5);
Console.WriteLine("Значение Num: {0}", getNum());
Console.ReadKey();
}
}
}
Когда приведённый выше код компилируется и выполняется, он выдаёт следующий результат:
Value of Num: 35 Value of Num: 175
Многоадресная рассылка делегата
Объекты делегатов можно использовать+; они объединяются с помощью оператора.
Объединённый делегат вызывает два делегата, которые он объединяет; объединять можно только делегаты одного типа.
-Оператор можно использовать для удаления делегата-компонента из объединённого делегата.
Используя эту полезную особенность делегатов, можно создать список вызова методов, которые будут вызваны при вызове делегата. Это называется делегатскоймногоадресной рассылкой (multicasting)также называемой многоадресной рассылкой.
Следующая программа демонстрирует многоадресную рассылку делегатов:
Пример
delegate int NumberChanger(int n);
namespace DelegateAppl
{
class TestDelegate
{
static int num = 10;
public static int AddNum(int p)
{
num += p;
return num;
}
public static int MultNum(int q)
{
num *= q;
return num;
}
public static int getNum()
{
return num;
}
static void Main(string[] args)
{
// Создать экземпляр делегата
NumberChanger nc;
NumberChanger nc1 = new NumberChanger(AddNum);
NumberChanger nc2 = new NumberChanger(MultNum);
nc = nc1;
nc += nc2;
// Вызов многоадресной рассылки
nc(5);
Console.WriteLine("Значение Num: {0}", getNum());
Console.ReadKey();
}
}
}
Когда приведённый выше код компилируется и выполняется, он выдаёт следующий результат:
Value of Num: 75
Назначение делегата
Следующий пример демонстрирует использование делегатов. ДелегатprintStringможет использоваться для ссылки на метод, который принимает строку в качестве входных данных и ничего не возвращает.
Мы используем этот делегат для вызова двух методов: первый выводит строку в консоль, а второй выводит строку в файл:
Пример
using System.IO;
namespace DelegateAppl
{
class PrintString
{
static FileStream fs;
static StreamWriter sw;
// Объявление делегата
public delegate void printString(string s);
// Этот метод выводит на консоль
public static void WriteToScreen(string str)
{
Console.WriteLine("Строка: {0}", str);
}
// Этот метод записывает в файл
public static void WriteToFile(string s)
{
fs = new FileStream("c:\\message.txt", FileMode.Append, FileAccess.Write);
sw = new StreamWriter(fs);
sw.WriteLine(s);
sw.Flush();
sw.Close();
fs.Close();
}
// Этот метод принимает делегат в качестве параметра и использует его для вызова метода
public static void sendString(printString ps)
{
ps("Hello World");
}
static void Main(string[] args)
{
printString ps1 = new printString(WriteToScreen);
printString ps2 = new printString(WriteToFile);
sendString(ps1);
sendString(ps2);
Console.ReadKey();
}
}
}
Когда приведённый выше код компилируется и выполняется, он выдаёт следующий результат:
The String is: Hello World
Удаление делегата
Если метод больше не нужен, можно использовать-=оператор, чтобы удалить метод из цепочки делегатов.
Пример
{
public delegate void PrintMessage(string message);
public static void PrintUpperCase(string message)
{
Console.WriteLine(message.ToUpper());
}
public static void PrintLowerCase(string message)
{
Console.WriteLine(message.ToLower());
}
public static void Main()
{
PrintMessage print = PrintUpperCase;
print += PrintLowerCase;
// Удалить метод PrintLowerCase
print -= PrintLowerCase;
// Вызов делегата (вызывает только PrintUpperCase)
print("Hello, C#"); // Вывод: HELLO, C#
}
}
Делегаты и события
Делегаты часто используются вместе с событиями. Событие — это специальный тип делегата, используемый для механизма публикации/подписки.
В C# событие по сути представляет собой тип, который инкапсулирует делегат, и используется для реагирования на определённые операции в программе.
Пример
{
// Определить событие
public event EventHandler Click;
// Метод для вызова события
public void OnClick()
{
if (Click != null)
{
Click(this, EventArgs.Empty); // Вызвать событие
}
}
}
public class Program
{
public static void Main()
{
Button button = new Button();
// Подписка на событие
button.Click += Button_Click;
// Вызвать событие
button.OnClick(); // Вывод "Button clicked!"
}
private static void Button_Click(object sender, EventArgs e)
{
Console.WriteLine("Кнопка нажата!");
}
}
Типы делегатов
C# предоставляет несколько распространённых типов делегатов:
1. Action
Action: Представляет метод, который не возвращает значение. Может принимать до 16 параметров.
Action<string> printMessage = Console.WriteLine;
printMessage("Hello");
2. Func
Func: Представляет метод, который имеет возвращаемое значение. Принимает до 16 параметров; первый параметр — входной, а последний параметр — тип возвращаемого значения.
Func<int, int, int> add = (x, y) => x + y; Console.WriteLine(add(3, 4)); // 输出 7
3. Predicate
Predicate: Представляет метод, который возвращает значение bool, обычно используется для проверки условий.
Predicate<int> isEven = x => x % 2 == 0; Console.WriteLine(isEven(4)); // 输出 True
Примечания о делегатах
Типобезопасность:Делегаты типобезопасны, то есть делегату можно назначить только методы с совпадающими сигнатурами.
Анонимные методы и лямбда-выражения: можно использовать анонимные методы или лямбда-выражения для создания экземпляров делегатов, что упрощает код.
Func<int, int, int> add = (x, y) => x + y; Console.WriteLine(add(5, 3)); // 输出 8
Асинхронный вызов: делегаты можно использовать вместе с методами BeginInvoke и EndInvoke для выполнения асинхронных вызовов.
Делегаты — очень мощная и гибкая возможность C#, которая помогает реализовывать событийно-ориентированное программирование, механизмы обратных вызовов и стили функционального программирования. Они не только предоставляют возможность повторного использования кода, но и повышают модульность программ. Понимание и освоение использования делегатов очень важно для программирования на C#.
Другие расширения