Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Делегат определяет тип, представляющий ссылки на методы с определенным списком параметров и типом возврата. Метод (статический или экземплярный) с совпадающим списком параметров и возвращаемым типом может быть присвоен переменной этого типа, а затем вызван напрямую (с соответствующими аргументами) или передан в качестве аргумента другому методу, а затем вызван. Использование делегата демонстрируется в следующем примере.
using System;
using System.Linq;
public class Program
{
public delegate string Reverse(string s);
static string ReverseString(string s)
{
return new string(s.Reverse().ToArray());
}
static void Main(string[] args)
{
Reverse rev = ReverseString;
Console.WriteLine(rev("a string"));
}
}
- Строка
public delegate string Reverse(string s);создает тип делегата метода, который принимает строковый параметр, а затем возвращает строковый параметр. - Метод
static string ReverseString(string s), имеющий точно тот же список параметров и тип возвращаемого значения, что и определенный тип делегата, реализует делегат. - В строке
Reverse rev = ReverseString;показано, что метод можно назначить переменной соответствующего типа делегата. - В
Console.WriteLine(rev("a string"));строке показано, как использовать переменную типа делегата для вызова делегата.
Чтобы упростить процесс разработки, .NET включает набор типов делегатов, которые программисты могут повторно использовать и не должны создавать новые типы. Эти типы — Func<>, Action<> и Predicate<>, и они могут использоваться без необходимости определять новые типы делегатов. Существуют некоторые различия между тремя типами, которые связаны с способом их использования:
-
Action<>используется, если требуется выполнить действие с помощью аргументов делегата. Метод, который он инкапсулирует, не возвращает значение. -
Func<>обычно используется при наличии преобразования, то есть необходимо преобразовать аргументы делегата в другой результат. Проекции являются хорошим примером. Метод, который он инкапсулирует, возвращает указанное значение. -
Predicate<>используется, когда необходимо определить, соответствует ли аргумент условию делегата. Также это можно записать какFunc<T, bool>, что означает, что метод возвращает значение типа boolean.
Теперь мы можем взять приведенный выше пример и переписать его с помощью Func<> делегата вместо пользовательского типа. Программа будет продолжать работать точно так же.
using System;
using System.Linq;
public class Program
{
static string ReverseString(string s)
{
return new string(s.Reverse().ToArray());
}
static void Main(string[] args)
{
Func<string, string> rev = ReverseString;
Console.WriteLine(rev("a string"));
}
}
В этом простом примере метод, определенный вне Main метода, кажется немного лишним. Платформа .NET Framework 2.0 представила концепцию анонимных делегатов, что позволяет создавать "встроенные" делегаты, не указывая дополнительный тип или метод.
В следующем примере анонимный делегат фильтрует список только по четным числам, а затем выводит их в консоль.
using System;
using System.Collections.Generic;
public class Program
{
public static void Main(string[] args)
{
List<int> list = new List<int>();
for (int i = 1; i <= 100; i++)
{
list.Add(i);
}
List<int> result = list.FindAll(
delegate (int no)
{
return (no % 2 == 0);
}
);
foreach (var item in result)
{
Console.WriteLine(item);
}
}
}
Как видно, тело делегата — это просто набор выражений, как и у любого другого делегата. Но вместо того, чтобы быть отдельным определением, мы представили его специально в нашем вызове метода .
Однако даже с этим подходом существует еще много кода, который мы можем выбросить. Вот где лямбда-выражения вступают в игру. Лямбда-выражения, или просто "лямбды", были введены в C# 3.0 в качестве одного из основных строительных блоков Language Integrated Query (LINQ). Это просто более удобный синтаксис для использования делегатов. Они объявляют список параметров и тело метода, но не имеют собственной формальной идентичности, если только они не назначены делегату. В отличие от делегатов, они могут быть непосредственно назначены в качестве правой стороны регистрации событий или в различных предложениях и методах LINQ.
Так как лямбда-выражение является просто другим способом указания делегата, мы должны иметь возможность переписать приведенный выше пример, чтобы использовать лямбда-выражение вместо анонимного делегата.
using System;
using System.Collections.Generic;
public class Program
{
public static void Main(string[] args)
{
List<int> list = new List<int>();
for (int i = 1; i <= 100; i++)
{
list.Add(i);
}
List<int> result = list.FindAll(i => i % 2 == 0);
foreach (var item in result)
{
Console.WriteLine(item);
}
}
}
В предыдущем примере используется лямбда-выражение i => i % 2 == 0. Опять же, это просто удобный синтаксис для использования делегатов. Что происходит под обложкой, похоже на то, что происходит с анонимным делегатом.
Опять же, лямбда-коды — это просто делегаты, что означает, что их можно использовать в качестве обработчика событий без каких-либо проблем, как показано в следующем фрагменте кода.
public MainWindow()
{
InitializeComponent();
Loaded += (o, e) =>
{
this.Title = "Loaded";
};
}
Оператор += в этом контексте используется для подписки на событие. Дополнительные сведения см. в разделе Как подписаться и отменить подписку на события.