Por que os delegados são tipos de referência?

Nota rápida sobre a resposta aceita: Eu discordo de uma pequena parte de Resposta de Jeffrey, ou seja, o ponto que desdeDelegate tinha que ser um tipo de referência, segue-se que todos os delegados são tipos de referência. (Simplesmente não é verdade que uma cadeia de herança multinível exclua tipos de valor; todos os tipos de enumeração, por exemplo, herdam deSystem.Enum, que por sua vez herda deSystem.ValueType, que herda deSystem.Object, todo tipos de referência.) No entanto, acho que o fato de que, fundamentalmente, todos os delegados de fato não herdam apenas deDelegate mas deMulticastDelegate é a realização crítica aqui. ComoRaymond aponta em um comentário paradel answer, uma vez que você se comprometeu a oferecer suporte a vários inscritos, não há realmente sentido emnã usando um tipo de referência para o próprio delegado, dada a necessidade de uma matriz em algum luga

Veja a atualização na parte inferio

Sempre me pareceu estranho que, se eu fizer isso:

Action foo = obj.Foo;

Estou criando umNov Action objeto, toda vez. Tenho certeza de que o custo é mínimo, mas envolve alocação de memória para posteriormente ser coletada como lix

Dado que os delegados são inerentementesi mesmo imutável, gostaria de saber por que eles não poderiam ser tipos de valor? Então, uma linha de código como a acima incorreria em nada além de uma simples atribuição a um endereço de memória na pilha *.

Mesmo considerando funções anônimas, parece (parami) isso funcionaria. Considere o seguinte exemplo simple

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

Nesse casofoo constitui umfech, sim. E, em muitos casos, imagino que isso exija um tipo de referência real (como quando variáveis locais são fechadas e modificadas dentro do fechamento). Mas, em alguns casos, não deveria. Por exemplo, no caso acima, parece que um tipo para apoiar o fechamento pode ser assim: Retiro meu argumento original sobre isso. O abaixo realmente precisa ser um tipo de referência (ou: não faznecessidad ser, mas se for umstruct só vai ser encaixotado de qualquer maneira). Portanto, desconsidere o exemplo de código abaixo. Deixo apenas para fornecer contexto para respostas que mencionam especificament

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;

Esta pergunta faz sentido? A meu ver, existem duas explicações possíveis:

implementação de delegados corretamente como tipos de valor exigiria trabalho / complexidade adicional, pois o suporte a coisas como fechamentos queFa modificar valores de variáveis locais teria exigido tipos de referência gerados pelo compilador de qualquer maneirHá algunsde outro razões pelas quais, sob o capô, os delegados simplesmente não pode ser implementado como tipos de valo

No final, não estou perdendo o sono por isso; é algo que me interessa há um tempo.

Atualiza: Em resposta ao comentário de Ani, vejo por que oCompilerGenerated type no meu exemplo acima também pode ser um tipo de referência, pois se um delegado incluir um ponteiro de função e um objeto, ele precisará de um tipo de referência de qualquer maneira (pelo menos para funções anônimas usando closures, pois mesmo se você introduziu um parâmetro de tipo genérico adicional - por exemplo,Action<TCaller> - isso não abrange tipos que não podem ser nomeados!).Contud, tudo isso faz com que eu me arrependa de trazer a questão dos tipos gerados pelo compilador para encerrar a discussão! Minha principal pergunta é sobre delegados, ou seja, a coisaco o ponteiro de função e o ponteiro de objeto. Ainda me parecenaquel poderia ser um tipo de valor.

Em outras palavras, mesmo que isso ...

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

... requer a criação de objeto de tipo de referência (para apoiar o fechamento e fornecer ao delegado algo para referência), por que ele requer a criação dedoi (o objeto de suporte ao fechamentomai aAction delegar)?

* Sim, sim, detalhes de implementação, eu sei! Tudo o que eu realmente quero dizer é armazenamento de memória de curto prazo.