Что из нижеперечисленного можно передать в конструктор делегата

Обновлено: 23.04.2024

Некоторые сведения относятся к предварительной версии продукта, в которую до выпуска могут быть внесены существенные изменения. Майкрософт не предоставляет никаких гарантий, явных или подразумеваемых, относительно приведенных здесь сведений.

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

Примеры

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

Комментарии

Класс Delegate является базовым классом для типов делегатов. Однако только системные и компиляторы могут быть явно производными от Delegate класса или от MulticastDelegate класса. Кроме того, не допускается наследовать новый тип от типа делегата. Класс Delegate не считается типом делегата; это класс, используемый для получения типов делегатов.

Большинство языков реализуют ключевое delegate слово и компиляторы для этих языков могут быть производными от MulticastDelegate класса, поэтому пользователи должны использовать delegate ключевое слово, предоставленное языком.

Среда CLR предоставляет каждому типу делегата и BeginInvoke EndInvoke методам для включения асинхронного вызова делегата. Дополнительные сведения об этих методах см. в статье "Асинхронные вызовы синхронных методов".

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

Метод экземпляра типа и целевого объекта, назначаемого данному типу.

Метод экземпляра типа с скрытым this параметром, предоставляемым в списке формальных параметров. Делегат считается делегатом открытого экземпляра.

Статический метод и целевой объект, присваиваемые первому параметру метода. Делегат, как утверждается, закрыт из-за своего первого аргумента.

Дополнительные сведения о привязке делегата см. в описании перегрузки CreateDelegate(Type, Object, MethodInfo, Boolean) метода.

Когда делегат представляет метод экземпляра, закрытый по его первому аргументу (наиболее распространенный случай), делегат сохраняет ссылку на точку входа метода и ссылку на объект, называемый целевым объектом, который является типом, который можно назначить типу, определяемому методом. Когда делегат представляет открытый метод экземпляра, он сохраняет ссылку на точку входа метода. Подпись делегата должна содержать скрытый this параметр в его формальном списке параметров. В этом случае делегат не имеет ссылки на целевой объект, а целевой объект должен быть предоставлен при вызове делегата.

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

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

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

Объединение операций, таких как Combine и Remove, не изменяет существующие делегаты. Вместо этого такая операция возвращает новый делегат, содержащий результаты операции, без изменений делегат или null . Операция объединения возвращается null , когда результат операции является делегатом, который не ссылается по крайней мере на один метод. Операция объединения возвращает без изменений делегат, если запрошенная операция не действует.

Универсальные делегаты, совместимые с назначением из-за дисперсии, необязательно объединяются. Для объединения типы должны точно соответствовать. Например, предположим, что именованный Derived класс является производным от класса с именем Base . Делегат типа Action ( Action(Of Base) в Visual Basic) может быть назначен переменной типа Action , но два делегата не могут быть объединены, так как типы не соответствуют точно.

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

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

Ближайшим эквивалентом делегата в C является указатель функции. Делегат может представлять статический метод или метод экземпляра. Когда делегат представляет метод экземпляра, делегат сохраняет не только ссылку на точку входа метода, но и ссылку на экземпляр класса. В отличие от указателей функций, делегаты являются объектно-ориентированными и типобезопасны.

Конструкторы

Инициализирует делегат, вызывающий заданный метод экземпляра указанного класса.

Инициализирует делегат, вызывающий заданный статистический метод указанного класса.

Свойства

Возвращает метод, представленный делегатом.

Возвращает экземпляр класса, метод которого вызывает текущий делегат.

Методы

Создает неполную копию делегата.

Сцепляет списки вызовов двух делегатов.

Сцепляет списки вызовов массива делегатов.

Сцепляет списки вызовов заданного группового (комбинируемого) делегата и текущего группового (комбинируемого) делегата.

Создает делегат указанного типа, представляющий заданный статический метод.

Создает делегат указанного типа, представляющий заданный статический метод, с заданным поведением на случай, если операция связывания завершится неудачей.

Создает делегат указанного типа, представляющий заданный статический метод или метод экземпляра, с заданным первым аргументом.

Создает делегат указанного типа, представляющий заданный статический метод или метод экземпляра, с заданным первым аргументом и поведением на случай, если операция связывания завершится неудачей.

Создает делегат указанного типа, представляющий заданный метод экземпляра, который вызывается для заданного экземпляра класса.

Создает делегат указанного типа, представляющий заданный метод экземпляра, который вызывается из заданного экземпляра класса с заданной установкой учета регистра.

Создает делегат указанного типа, представляющий заданный статический метод, вызываемый для заданного экземпляра класса с заданной установкой учета регистра и заданным поведением на случай, если операция связывания завершится неудачей.

Создает делегат указанного типа, представляющий заданный статический метод заданного класса.

Создает делегат указанного типа, представляющий заданный статический метод заданного класса с заданной установкой учета регистра.

Создает делегат заданного типа, представляющий заданный статический метод заданного класса с заданными установками учета регистра и поведением на случай, если операция связывания завершится неудачей.

Динамически (с поздней привязкой) вызывает метод, представленный текущим делегатом.

Динамически (с поздней привязкой) вызывает метод, представленный текущим делегатом.

Определяет, принадлежат ли заданный объект и текущий делегат к одному типу, и одинаковы ли их целевые объекты, методы и списки вызовов.

Возвращает хэш-код делегата.

Возвращает список вызовов делегата.

Возвращает статический метод, представленный текущим делегатом.

Возвращает объект Type для текущего экземпляра.

Создает неполную копию текущего объекта Object.

Удаляет последнее вхождение списка вызовов делегата из списка вызовов другого делегата.

Удаляет все вхождения списка вызовов одного делегата из списка вызовов другого делегата.

Удаляет список вызовов одного делегата из списка вызовов другого делегата.

Возвращает строку, представляющую текущий объект.

Операторы

Определяет, равны ли два заданных делегата.

Определяет, являются ли заданные делегаты неравными.

Методы расширения

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

Делегат — это тип, который безопасно инкапсулирует метод, схожий с указателем функции в C и C++. В отличие от указателей функций в C делегаты объектно-ориентированы, типобезопасны и безопасны. Тип делегата задается его именем. В следующем примере объявляется делегат с именем Del , который может инкапсулировать метод, использующий в качестве аргумента значение string и возвращающий значение void:

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

Обратный вызов также часто используется для задания настраиваемого метода сравнения и передачи этого делегата в метод сортировки. Это позволяет сделать коду вызывающего объекта частью алгоритма сортировки. В следующем примере метод использует тип Del тип в качестве параметра.

Затем в данный метод можно передать созданный ранее делегат:

и получить следующие выходные данные в окне консоли:

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

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

Вместе с рассмотренным ранее статическим методом DelegateMethod есть три метода, для которых можно создать оболочку с помощью экземпляра Del .

На данном этапе список вызова делегата allMethodsDelegate содержит три метода — Method1 , Method2 и DelegateMethod . Три исходных делегата d1 , d2 и d3 остаются без изменений. При вызове allMethodsDelegate все три метода вызываются по порядку. Если делегат использует параметры, передаваемые по ссылке, эта ссылка передается после каждого из трех методов, а все изменения одного из методов становятся видны в следующем методе. Если любой из методов вызывает неперехваченное исключение, это исключение передается в вызывающий делегат объект, а последующие методы в списке вызова не вызываются. Если делегат имеет возвращаемое значение и (или) выходные параметры, он возвращает возвращаемое значение и параметры последнего вызванного метода. Чтобы удалить метод из списка вызовов, используйте вычитание или операторы присваивания вычитания ( - или -= ). Пример:

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

Делегаты, в списке вызова которых находятся несколько методов, является производным от MulticastDelegate, являющегося подклассом класса System.Delegate . Приведенный выше код работает в любом из случаев, так как оба класса поддерживают GetInvocationList .

Назначение сравнения делегатов двух различных типов во время компиляции вызовет ошибку компиляции. Если экземпляры делегата статически относятся к типу System.Delegate , сравнение допустимо, но во время выполнения будет возвращено значение false. Пример:

Делегат — это тип, который представляет ссылки на методы с определенным списком параметров и типом возвращаемого значения. При создании экземпляра делегата этот экземпляр можно связать с любым методом с совместимой сигнатурой и типом возвращаемого значения. Метод можно вызвать (активировать) с помощью экземпляра делегата.

Делегаты используются для передачи методов в качестве аргументов к другим методам. Обработчики событий — это ничто иное, как методы, вызываемые с помощью делегатов. При создании пользовательского метода класс (например, элемент управления Windows) может вызывать этот метод при появлении определенного события. В следующем примере показано объявление делегата:

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

В контексте перегрузки метода его сигнатура не содержит возвращаемое значение. Однако в контексте делегатов сигнатура метода содержит возвращаемое значение. Другими словами, метод должен иметь тот же возвращаемый тип, что и делегат.

Благодаря возможности ссылаться на метод как на параметр делегаты идеально подходят для определения методов обратного вызова. Можно написать метод, сравнивающий два объекта в приложении. Этот метод можно использовать в делегате для алгоритма сортировки. Поскольку код сравнения отделен от библиотеки, метод сортировки может быть более общим.

Общие сведения о делегатах

Делегаты имеют следующие свойства.

  • Делегаты подобны указателям на функции в C++, но являются полностью объектно-ориентированными и, в отличие от указателей C++ на функции-члены, инкапсулируют экземпляр объекта вместе с методом.
  • Делегаты допускают передачу методов в качестве параметров.
  • Делегаты можно использовать для определения методов обратного вызова.
  • Делегаты можно связывать друг с другом; например, при появлении одного события можно вызывать несколько методов.
  • Точное соответствие методов типу делегата не требуется. Дополнительные сведения см. в разделе Использование вариативности в делегатах.
  • Для краткой записи встроенных блоков кода введены лямбда-выражения. В результате компиляции лямбда-выражений (в определенном контексте) получаются типы делегатов. Дополнительные сведения о лямбда-выражениях см. в разделе Лямбда-выражения.

В этом разделе

Делегаты представляют такие объекты, которые указывают на методы. То есть делегаты - это указатели на методы и с помощью делегатов мы можем вызвать данные методы.

Определение делегатов

Для объявления делегата используется ключевое слово delegate , после которого идет возвращаемый тип, название и параметры. Например:

Делегат Message в качестве возвращаемого типа имеет тип void (то есть ничего не возвращает) и не принимает никаких параметров. Это значит, что этот делегат может указывать на любой метод, который не принимает никаких параметров и ничего не возвращает.

Рассмотрим примение этого делегата:

Прежде всего сначала необходимо определить сам делегат:

Для использования делегата объявляется переменная этого делегата:

Далее в делегат передается адрес определенного метода (в нашем случае метода Hello). Обратите внимание, что данный метод имеет тот же возвращаемый тип и тот же набор параметров (в данном случае отсутствие параметров), что и делегат.

Затем через делегат вызываем метод, на который ссылается данный делегат:

Вызов делегата производится подобно вызову метода.

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

Место определения делегата

Либо вне класса:

Параметры и результат делегата

Рассмотрим определение и применение делегата, который принимает параметры и возвращает результат:

В данном случае делегат Operation возвращает значение типа int и имеет два параметра типа int. Поэтому этому делегату соответствует любой метод, который возвращает значение типа int и принимает два параметра типа int. В данном случае это методы Add и Multiply. То есть мы можем присвоить переменной делегата любой из этих методов и вызывать.

Поскольку делегат принимает два параметра типа int, то при его вызове необходимо передать значения для этих параметров: operation(4,5) .

Присвоение ссылки на метод

Выше переменной делегата напрямую присваивался метод. Есть еще один способ - создание объекта делегата с помощью конструктора, в который передается нужный метод:

Оба способа равноценны.

Соответствие методов делегату

Как было написано выше, методы соответствуют делегату, если они имеют один и тот же возвращаемый тип и один и тот же набор параметров. Но надо учитывать, что во внимание также принимаются модификаторы ref , in и out . Например, пусть у нас есть делегат:

Этому делегату соответствует, например, следующий метод:

А следующие методы НЕ соответствуют:

Здесь метод SomeMethod2 имеет другой возвращаемый тип, отличный от типа делегата. SomeMethod3 имеет другой набор параметров. Параметры SomeMethod4 и SomeMethod5 также отличаются от параметров делегата, поскольку имеют модификаторы ref и out.

Добавление методов в делегат

В примерах выше переменная делегата указывала на один метод. В реальности же делегат может указывать на множество методов, которые имеют ту же сигнатуру и возвращаемые тип. Все методы в делегате попадают в специальный список - список вызова или invocation list. И при вызове делегата все методы из этого списка последовательно вызываются. И мы можем добавлять в этот список не один, а несколько методов. Для добавления методов в делегат применяется операция += :

В данном случае в список вызова делегата message добавляются два метода - Hello и HowAreYou. И при вызове message вызываются сразу оба этих метода.

Однако стоит отметить, что в реальности будет происходить создание нового объекта делегата, который получит методы старой копии делегата и новый метод, и новый созданный объект делегата будет присвоен переменной message.

При добавлении делегатов следует учитывать, что мы можем добавить ссылку на один и тот же метод несколько раз, и в списке вызова делегата тогда будет несколько ссылок на один и то же метод. Соответственно при вызове делегата добавленный метод будет вызываться столько раз, сколько он был добавлен:

Подобным образом мы можем удалять методы из делегата с помощью операций -= :

При удалении методов из делегата фактически будет создаватья новый делегат, который в списке вызова методов будет содержать на один метод меньше.

Стоит отметить, что при удалении метода может сложиться ситуация, что в делегате не будет методов, и тогда переменная будет иметь значение null. Поэтому в данном случае переменная определена не просто как переменная типа Message , а именно Message? , то есть типа, который может представлять как делегат Message, так и значение null.

Кроме того, перед вторым вызовом мы проверяем переменную на значение null.

При удалении следует учитывать, что если делегат содержит несколько ссылок на один и тот же метод, то операция -= начинает поиск с конца списка вызова делегата и удаляет только первое найденное вхождение. Если подобного метода в списке вызова делегата нет, то операция -= не имеет никакого эффекта.

Объединение делегатов

Делегаты можно объединять в другие делегаты. Например:

В данном случае объект mes3 представляет объединение делегатов mes1 и mes2. Объединение делегатов значит, что в список вызова делегата mes3 попадут все методы из делегатов mes1 и mes2. И при вызове делегата mes3 все эти методы одновременно будут вызваны.

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

В примерах выше делегат вызывался как обычный метод. Если делегат принимал параметры, то при его вызове для параметров передавались необходимые значения:

Другой способ вызова делегата представляет метод Invoke() :

Если делегат принимает параметры, то в метод Invoke передаются значения для этих параметров.

Следует учитывать, что если делегат пуст, то есть в его списке вызова нет ссылок ни на один из методов (то есть делегат равен Null), то при вызове такого делегата мы получим исключение, как, например, в следующем случае:

Поэтому при вызове делегата всегда лучше проверять, не равен ли он null. Либо можно использовать метод Invoke и оператор условного null:

Если делегат возвращает некоторое значение, то возвращается значение последнего метода из списка вызова (если в списке вызова несколько методов). Например:

Обобщенные делегаты

Делегаты, как и другие типы, могут быть обобщенными, например:

Здесь делегат Operation типизируется двумя параметрами типов. Параметр T представляет тип возвращаемого значения. А параметр K представляет тип передаваемого в делегат параметра. Таким образом, этому делегату соответствует метод, который принимает параметр любого типа и возвращает значение любого типа.

В прогамме мы можем определить переменные делегата под определенный метод. Например, делегату Operation соответствует метод, который принимает число int и возвращает число типа decimal. А делегату Operation соответствует метод, который принимает и возвращает число типа int.

Делегаты как параметры методов

Также делегаты могут быть параметрами методов. Благодаря этому один метод в качестве параметров может получать действия - другие методы. Например:

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

При вызове метода DoOperation мы можем передать в него в качестве третьего параметра метод, который соответствует делегату Operation.

Возвращение делегатов из метода

Также делегаты можно возвращать из методов. То есть мы можем возвращать из метода какое-то действие в виде другого метода. Например:

В данном случае метод SelectOperation() в качестве параметра принимает перечисление типа OperationType. Это перечисление хранит три константы, каждая из которых соответствует определенной арифметической операции. И в самом методе в зависимости от значения параметра возвращаем определенный метод. Причем поскольку возвращаемый тип метода - делегат Operation, то метод должен возвратить метод, который соответствует этому делегату - в нашем случае это методы Add, Subtract, Multiply. То есть если параметр метода SelectOperation равен OperationType.Add , то возвращается метод Add, который выолняет сложение двух чисел:

При вызове метода SelectOperation мы можем получить из него нужное действие в переменную operation:

И при вызове переменной operation фактически будет вызываться полученный из SelectOperation метод:

В прошлой теме подробно были рассмотрены делегаты. Однако данные примеры, возможно, не показывают истинной силы делегатов, так как нужные нам методы в данном случае мы можем вызвать и напрямую без всяких делегатов. Однако наиболее сильная сторона делегатов состоит в том, что они позволяют делегировать выполнение некоторому коду извне. И на момент написания программы мы можем не знать, что за код будет выполняться. Мы просто вызываем делегат. А какой метод будет непосредственно выполняться при вызове делегата, будет решаться потом.

Рассмотрим подробный пример. Пусть у нас есть класс, описывающий счет в банке:

В переменной sum хранится сумма на счете. С помощью конструктора устанавливается начальная сумма на счете. Метод Add() служит для добавления на счет, а метод Take - для снятия денег со счета.

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

Но что если наш класс планируется использовать в других проектах, например, в графическом приложении на Windows Forms или WPF, в мобильном приложении, в веб-приложении. Там строка уведомления

не будет иметь большого смысла.

Более того, наш класс Account будет использоваться другими разработчиками в виде отдельной библиотеки классов. И эти разработчики захотят уведомлять о снятии средств каким-то другим образом, о которых мы даже можем не догадываться на момент написания класса. Поэтому примитивое уведомление в виде строки кода

не самое лучшее решение в данном случае. И делегаты позволяют делегировать определение действия из класса во внешний код, который будет использовать этот класс.

Изменим класс, применив делегаты:

Для делегирования действия здесь определен делегат AccountHandler . Этот делегат соответствует любым методам, которые имеют тип void и принимают параметр типа string .

В классе Account определяем переменную taken , которая представляет этот делегат:

Теперь надо связать эту переменную с конкретным действием, которое будет выполняться. Мы можем использовать разные способы для передачи делегата в класс. В данном случае определяется специальный метод RegisterHandler, который передается в переменную taken реальное действие:

Таким образом, делегат установлен, и теперь его можно вызывать. Вызов делегата производится в методе Take:

Теперь протестируем класс в основной программе:

Здесь через метод RegisterHandler переменной taken в классе Account передается ссылка на метод PrintSimpleMessage . Этот метод соответствует делегату AccountHandler. Соответственно там, где вызывается делегат taken в методе Account, в реальности будет выполняться метод PrintSimpleMessage.

Добавление и удаление методов в делегате

Хотя в примере наш делегат принимал адрес на один метод, в действительности он может указывать сразу на несколько методов. Кроме того, при необходимости мы можем удалить ссылки на адреса определенных методов, чтобы они не вызывались при вызове делегата. Итак, изменим в классе Account метод RegisterHandler и добавим новый метод UnregisterHandler, который будет удалять методы из списка методов делегата:

В первом методе объединяет делегаты taken и del в один, который потом присваивается переменной taken . Во втором методе из переменной taken удаляется делегат del .

Применим новые методы:

В строке account.UnregisterHandler(PrintColorMessage); этот метод удаляется из списка вызовов делегата, поэтому этот метод больше не будет срабатывать. Консольный вывод будет иметь следующую форму:

Читайте также: