Делегаты
При использовании механизма обратного вызова приходится выполнять вспомогательные операции для регистрации вызываемых функций. В оставшейся части главы будет показано, что при этом происходит и как при помощи этих операций добиться максимальной эффективности обратного вызова.
Механизм обратного вызова (а следовательно, и события) в VB.NET зависит от особой разновидности объектов .NET, называемых делегатами. Делегат является экземпляром класса System.Delegate. В простейшем случае в делегате инкапсулируется объект и адрес заданной функции или процедуры этого объекта. Такие делегаты идеально подходят для схем обратного вызова вроде той, что используется при обработке событий. Почему? Потому что делегат содержит всю информацию, необходимую для обратного вызова, и может использоваться для вызова нужного метода объекта-приемника.
Но прежде, чем переходить к описанию работы с делегатами, стоит подчеркнуть одно важное обстоятельство. Хотя обработка событий на платформе .NET основана на использовании делегатов, в подавляющем большинстве случаев вам не придется работать непосредственно с делегатами. Команда AddHandlег предоставляет в ваше распоряжение все необходимое для гибкой обработки событий в VB.NET (впрочем, как вы вскоре увидите, у делегатов есть и другие применения).
Примечание
В делегатах также могут инкапсулироваться общие методы класса без привязки к конкретному экземпляру. Кроме того, в групповых делегатах инкапсулируется сразу несколько объектов с несколькими процедурами.
Примечание
Традиционные средства вызова функции по адресу были основаны на языковой поддержке указателей на функции. Как показано в следующей врезке, применение указателей на функции сопряжено с потенциальным риском. По сравнению с указателями на функции делегаты обеспечивают дополнительную защиту, значение которой не стоит недооценивать.
Примечание
Указатели на функции в VB6. При вызовах функций API часто передается адрес функции для обратного вызова, поэтому в VB6 поддерживался оператор AddressOf. В VB6 адрес функции мог передаваться при любом вызове API. Но что происходило, если список параметров функции, адрес которой передавался при вызове, отличался от предполагаемого? Обычно это приводило к общей ошибке защиты (GPF) и даже к фатальным сбоям с появлением синего экрана.
Таким образом, произвольные указатели на функции обладают принципиальным недостатком: компилятор не может проверить, что такой указатель относится к функции правильного типа. Делегаты представляют собой разновидность указателей на функции, безопасных по отношению к типам. Следуя принципу "доверяй, но проверяй", компилятор автоматически проверяет сигнатуру вызываемой функции – такой вариант работает гораздо надежнее.