Артём в шагах "Шаг 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 ".