Почему делегаты ссылаются на типы?

Быстрая заметка о принятом ответе: Я не согласен с небольшой частьюОтвет Джеффриа именно то, что сDelegate должен был быть ссылочным типом, из этого следует, что все делегаты являются ссылочными типами. (Просто неверно, что многоуровневая цепочка наследования исключает типы значений; все типы перечислений, например, наследуются отSystem.Enumкоторый в свою очередь наследует отSystem.ValueType, который наследует отSystem.Object, все ссылочные типы.) Однако я думаю, что, по сути, все делегаты на самом деле наследуют не только отDelegate но изMulticastDelegate это критическая реализация здесь. КакРаймонд указывает в комментарии кего ответьте, если вы решили поддержать нескольких подписчиков,не использование ссылочного типа для самого делегата, учитывая необходимость где-то в массиве.

Смотрите обновление внизу.

Мне всегда казалось странным, что если я делаю это:

Action foo = obj.Foo;

Я создаюновый Action объект каждый раз. Я уверен, что стоимость минимальна, но она включает в себя выделение памяти для последующей сборки мусора.

Учитывая, что делегаты по своей сутисамих себя Неизменный, интересно, почему они не могут быть типами значений? Тогда строка кода, подобная приведенной выше, не повлечет за собой ничего, кроме простого назначения адреса памяти в стеке *.

Кажется, даже с учетом анонимных функцийменя) это будет работать. Рассмотрим следующий простой пример.

Action foo = () => { obj.Foo(); };

В этом случаеfoo действительно представляет собойзакрытие, да. И во многих случаях, я полагаю, это требует фактического ссылочного типа (например, когда локальные переменные закрываются и изменяются внутри замыкания).Но в некоторых случаях это не должно. Например, в приведенном выше случае кажется, что тип для поддержки замыкания может выглядеть следующим образом: Я забираю свое первоначальное мнение об этом. Ниже действительно должен быть ссылочный тип (или: это не такнужно быть, но если этоstruct это все равно будет в штучной упаковке). Поэтому не обращайте внимания на приведенный ниже пример кода. Я оставляю это только для того, чтобы предоставить контекст для ответов, которые конкретно упоминаются.

struct CompilerGenerated
{
    Obj obj;

    public CompilerGenerated(Obj obj)
    {
        this.obj = obj;
    }

    public void CallFoo()
    {
        obj.Foo();
    }
}

// ...elsewhere...

// This would not require any long-term memory allocation
// if Action were a value type, since CompilerGenerated
// is also a value type.
Action foo = new CompilerGenerated(obj).CallFoo;

Имеет ли этот вопрос смысл? На мой взгляд, есть два возможных объяснения:

Правильная реализация делегатов в качестве типов значений потребовала бы дополнительной работы / сложности, поскольку поддержка таких вещей, как замыкания, которыеделать В любом случае для изменения значений локальных переменных требуются сгенерированные компилятором ссылочные типы.Есть некоторыеДругие причины, почему под капотом делегаты простоне может быть реализованы как типы значений.

В конце концов, я не теряю сон из-за этого; это просто то, что мне было интересно на некоторое время.

ОбновитьВ ответ на комментарий Ани я понимаю, почемуCompilerGenerated тип в моем приведенном выше примере также может быть ссылочным типом, поскольку, если делегат будет содержать указатель на функцию и указатель объекта, ему в любом случае понадобится ссылочный тип (по крайней мере, для анонимных функций, использующих замыкания, поскольку даже если вы представили дополнительный параметр общего типа - например,Action<TCaller>- это не распространяется на типы, которые нельзя назвать!).тем не мениевсе, что это делает, отчасти заставляет меня сожалеть о том, что вообще привел в обсуждение вопрос о сгенерированных компилятором типах для замыканий! Мой главный вопрос оделегатыто есть вещьс участием указатель на функцию и указатель на объект. Мне все еще кажетсяэто может быть типом значения.

Другими словами, даже если это ...

Action foo = () => { obj.Foo(); };

... требует созданияодин объект ссылочного типа (чтобы поддержать закрытие и дать делегату что-то для ссылки), почему он требует созданиядва (закрывающий объектплюс Action Делегат)?

* Да, да, детали реализации, я знаю! Все, что я имею в виду, этократковременное хранение в памяти.

Ответы на вопрос(7)

Ваш ответ на вопрос