Поиск по сайту:

Как делегаты, действия и функции работают в C#?


Делегаты, действия и функции — все типы C#, ориентированные на схожую концепцию: хранение ссылок в переменной на функции и методы, которые можно вызывать позже для выполнения метода, на который они указывают. Мы обсудим различия между различными типами и способы их использования.

Что такое делегаты?

Делегаты — это, по сути, безопасные с точки зрения типов указатели на функции. Делегат — это просто переменная, которая указывает на метод и «делегирует» ему выполнение. Метод, на который он указывает, может быть именованным, как метод члена класса, или анонимным, как в случае со стрелочными функциями.

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

Затем вы можете написать реализацию или несколько реализаций, которые имеют одни и те же возвращаемые и входные параметры, но на самом деле содержат в себе код.

Затем вы можете использовать определение делегата как параметр типа, создав новую переменную, указывающую на реальную функцию. Когда вы хотите вызвать делегата, вы можете вызвать его как метод и передать ему правильные параметры. Среда выполнения .NET проверит, на какой метод указывает делегат, и вызовет этот метод.

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

Однако делегаты не должны указывать на именованные методы, поскольку они гораздо полезнее, когда указывают на анонимные функции. Они создаются с использованием синтаксиса стрелочных функций, () => {}, который определяет функцию без имени, следовательно, «анонимную». Вы можете использовать их так же, как обычные делегаты.

Действия и функции — это просто делегаты с определенными аргументами и возвращаемыми параметрами. Действия могут принимать любое количество аргументов, но возвращать void. Funcs может принимать любое количество аргументов, но всегда возвращает значение, определяемое последним параметром типа.

На самом деле это всего лишь готовые определения делегатов с общим типом T. Action без каких-либо типов – это просто пустое определение делегата с типом возвращаемого значения void.

Вы, вероятно, много использовали Funcs, не задумываясь об этом. Это функции, которые вы передаете перечисляемым запросам, таким как .Where(), для выбора элементов. Эти виды функций, в частности, называются предикатами, а Predicate функционально эквивалентно написанию Func. Однако на самом деле они не особенные, кроме определенного имени.

На самом деле вы можете строго определить Func, присвоить ему функцию в качестве значения и передать его в .Where(), что выглядит странно тревожным, учитывая вас. обычно делают это встроенным без определения типа или переменной, но это вполне допустимо:

Вы можете строго определить Predicate вместо Func, но некоторым функциям требуется либо одно, либо другое, и, к сожалению, между ними нет неявного преобразования. .

У делегатов есть несколько других интересных свойств. Поскольку это просто переменные, вы можете хранить их в списках и словарях, как и любой другой тип ссылки. Вы можете перебирать все делегаты в списке и вызывать каждого из них по очереди.

Вы также можете многоадресная рассылка делегатов, что означает, что вы можете добавлять их вместе. Например, в следующем примере определяется делегат Greeting, который принимает имя и приветствует пользователя. Определены две реализации: Hello – приветствие и Goodbye – прощание.

Затем вы можете буквально просто добавить их вместе с оператором со знаком плюс, хотя обратите внимание, что здесь ожидается делегат, а не группа методов, поэтому вам нужно обернуть имя функции в new DelegateDefinition() чтобы он работал правильно. Этот комбинированный делегат выполняет оба элемента в том порядке, в котором они были добавлены.

Вы можете вычесть делегаты, что удалит их из списка, хотя операторов умножения почему-то нет, и деление здесь не имеет никакого смысла.