Артём в шагах "Шаг 97 - Указатель на функцию delegate" и "Шаг 98 - Извещения EVENT" описывал делегаты, я не буду повторятся лишь напомню, что это аналог функций обратного вызова(callback) в C/C++. Но это не совсем так, давайте рассмотрим небольшую программку.
using System; internal class TestDelegate { public delegate void DelegateFoo(object obj, int IntValue); public static void StaticFoo(object obj, int IntValue) { Console.WriteLine("что - то делаем"); } public void InstatnceFoo(object obj, int IntValue) { Console.WriteLine("вводим данные {0} и {1}", obj, IntValue); } public DelegateFoo OurDelegate; } internal class App { [STAThreadAttribute] public static void Main() { TestDelegate test = new TestDelegate(); test.OurDelegate = new TestDelegate.DelegateFoo(TestDelegate.StaticFoo); test.OurDelegate("string", 128); test.OurDelegate += new TestDelegate.DelegateFoo(test.InstatnceFoo); test.OurDelegate("string", 128); } }
Ничего особенного, создаётся делегат который использует статическую функцию, вызывается. Потом создаём делегат на основе функции экземпляра класса плюсуем к тому, что был и вызываем(происходит вызов двух функций). Но, посмотрев CLR код, мы видим кое-что особенное.
Мы видим, что появился новый класс. Дело в следующем. Когда компилятор встречает
public delegate void DelegateFoo(object obj, int IntValue);
Он преобразует это в класс, в нашем случае он имеет вид
public class DelegateFoo : System.MulticastDelegate { //конструктор public DelegateFoo(Object target, Int32 methodPtr); //прототип нашего метода public void virtual Invoke(Object obj, Int32 IntVlaue); //далее идут методы которые используются для //"выполнения " делегата асинхронно //об этом я поговорю позже public virtual IAsyncResult BeginInvoke( Object obj, Int32 IntValue, AsyncCallback callback, Object object); public virtual void EndInvoke(IAsyncResult result); }
Обратите на выделенное жирным шрифтом. В конструктор передаются два параметра, первый это ссылка на объект, которому принадлежит метод(вроде бы так, но может быть я ошибся когда разбирался, всё таки английский не родной). Если метод статический как в первом случае.
test.OurDelegate = new TestDelegate.DelegateFoo(TestDelegate.StaticFoo);
То туда передаётся null. Второй параметр целое, которое идентифицирует наш метод в CLR(не путать с указателем на метод, хотя может быть это и указатель). Но мы передаём в конструктор совсем другое, по идее наш код компилироваться не будет, но компилятор сам всё туда передаёт с помощью "отражения"(reflection). Получается, что делегат на самом деле обёртка, вокруг метода который нам нужно вызвать.
MulticastDlegate имеет два public read only свойства Target и Method. Target это проперти которое возвращает ссылку на объект, в котором определена функция которая используется делегатом(если метод статической то получим null). Method возвращает System.Reflection.MethodInfo который идентифицирует callback метод. Это можно использовать например для проверки типа делегата:
Boolean TestInstanceMethodOfType( MulticastDelegate d, Type type) { return((d.Target != null) && d.Target.GetType == type); }
Или для проверки имени метода:
Boolean TestMethodOfName( MulticastDelegate d, String methodName) { return(d.Method.Name == methodName); }
Если вы посмотрите на код в ILDASM вы увидите, что мы не вызываем наш метод следующим образом
test.OurDelegate("string", 128);
Вместо этого подставляется следующая строка(не совсем такая, это так написал чтобы понятнее было)
test.OurDelegate.Invoke("string", 128);
Про "Асинхронную модель программирования " я расскажу в следующей статье. Примеры использования Target и Method взяты из статьи Jeffrey Richter " An Introduction to Delegates ".