На что указывает делегат?

Я прочитал, что ссылочный тип содержит ссылку на фактический объект, который может храниться в управляемой куче. Когда метод присваивается ссылочной переменной делегата, к какой памяти относится точка отсчета? Что этот блок памяти имеет отношение к фактическому функциональному коду?

Ответ 1

Давайте рассмотрим простой пример:

using System;
class Program
{
    delegate bool MyFilter(int x);

    bool IsOdd(int x)
    {
        return x % 2 == 1;
    }

    static void Main()
    {
        MyFilter f = new Program().IsOdd;
        Console.WriteLine(f(5));
    }
}

Что делает компилятор? Начните с этой строки:

delegate bool MyFilter(int x);

Компилятор генерирует тип, который выглядит примерно так:

class MyFilter : MulticastDelegate
{
    public MyFilter(Object thisRef, IntPtr method);
    public bool Invoke(int x);
    // BeginInvoke(), EndInvoke() - Let ignore those
}    

Тип MyFilter имеет конструктор, который принимает два параметра: IntPtr для тела метода для вызова и объект, на который должен быть вызван этот метод. Метод Invoke() в типе MyFilter вызывает фактический делегат.

Теперь давайте посмотрим, что происходит в методе Main(). Компилятор перепишет его примерно так:

    static void Main()
    {
        MyFilter f = new MyFilter(new Program(), addressof(Program.IsOdd));
        Console.WriteLine(f.Invoke(5));
    }

Конечно, addressof не является фактическим оператором С#, но вы можете себе представить, что он возвращает адрес тела для переданного метода. Кроме того, я не обсуждал другие темы, связанные с делегатами, такие как цепочка (что функция, предоставляемая базовым классом MulticastDelegate), но, надеюсь, я затронул ваш вопрос.

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

Ответ 2

На самом деле существует два типа делегатов: Delegate и MulticastDelegate. Это абстрактные классы, которые наследуются из фактического экземпляра delegate. Я бы порекомендовал читать эти классы, поскольку у них гораздо больше (точных) деталей, чем я объяснил здесь, но для короткой версии:

Хотя я не на 100% уверен, что реализация точно работает, самый простой способ думать о объекте delegate - это object и MethodInfo, который является типом отражения для определенного метода. Причина, по которой тип delegate является абстрактным, заключается в том, что метод Invoke зависит от параметров метода.

Аналогично, MulticastDelegate содержит несколько объектов delegate, каждая из которых указывает на индивидуальную комбинацию object/MethodInfo. Это позволяет системе event, при которой один event запускается при вызове нескольких методов.

Возвращаясь к деталям реализации, когда вы определяете делегат, он создает унаследованный класс от MulticastDelegate с помощью метода Invoke, а также object может быть null для статических методов.

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

Ответ 3

Не задавая чрезмерно сложный ответ, он указывает (делает ссылку) на фактический блок памяти, где хранится метод.

Ответ 4

Делегат фактически содержит два адреса - адрес метода, а также адрес для объекта. Это довольно интересно, и несколько патентов, связанных с делегатами, были присуждены Хельсбергу http://www.google.com/patents/US6185728

Он очень эффективен. В интервью Heljsberg указал, что он может быть более эффективным, чем отправка VTBL (поскольку это прямой указатель на функцию). В простейших случаях это буквально косвенный вызов. например

 jmp *%eax

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