Ескертпе
Бұл бетке кіру үшін қатынас шегін айқындау қажет. Жүйеге кіруді немесе каталогтарды өзгертуді байқап көруге болады.
Бұл бетке кіру үшін қатынас шегін айқындау қажет. Каталогтарды өзгертуді байқап көруге болады.
Делегат определяет тип, представляющий ссылки на методы с определенным списком параметров и типом возврата. Метод (статический или экземплярный) с совпадающим списком параметров и возвращаемым типом может быть присвоен переменной этого типа, а затем вызван напрямую (с соответствующими аргументами) или передан в качестве аргумента другому методу, а затем вызван. Использование делегата демонстрируется в следующем примере.
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";
};
}
Оператор += в этом контексте используется для подписки на событие. Дополнительные сведения см. в разделе Как подписаться и отменить подписку на события.