Я прочитал, что ссылочный тип содержит ссылку на фактический объект, который может храниться в управляемой куче. Когда метод присваивается ссылочной переменной делегата, к какой памяти относится точка отсчета? Что этот блок памяти имеет отношение к фактическому функциональному коду?
На что указывает делегат?
Ответ 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 команды для выполнения делегата.