¿Por qué los delegados son tipos de referencia?

Nota rápida sobre la respuesta aceptada: No estoy de acuerdo con una pequeña parte de La respuesta de Jeffrey, es decir, el punto que desdeDelegate tenía que ser un tipo de referencia, se deduce que todos los delegados son tipos de referencia. (Simplemente no es cierto que una cadena de herencia de varios niveles descarte los tipos de valor; todos los tipos de enumeración, por ejemplo, heredan deSystem.Enum, que a su vez hereda deSystem.ValueType, que hereda deSystem.Object, todo tipos de referencia.) Sin embargo, creo que el hecho de que, fundamentalmente, todos los delegados de hecho heredan no solo deDelegate Pero de dondeMulticastDelegate es la realización crítica aquí. ComoRaymond señala en un comentario as respuesta, una vez que se haya comprometido a admitir múltiples suscriptores, realmente no tiene sentido enn utilizando un tipo de referencia para el propio delegado, dada la necesidad de una matriz en alguna parte.

Ver actualización en la parte inferior.

Siempre me ha parecido extraño que si hago esto:

Action foo = obj.Foo;

Estoy creando unanuev Action objeto, cada vez. Estoy seguro de que el costo es mínimo, pero implica la asignación de memoria para luego recolectar basura.

Dado que los delegados son inherentementesí mismo inmutable, me pregunto por qué no podrían ser tipos de valor. Entonces, una línea de código como la anterior no implicaría más que una simple asignación a una dirección de memoria en la pila *.

ncluso considerando las funciones anónimas, parece (ay) esto funcionaría. Considere el siguiente ejemplo sencillo

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

En este casofoo constituye unacierr, si. Y en muchos casos, imagino que esto requiere un tipo de referencia real (como cuando las variables locales se cierran y se modifican dentro del cierre). @ Pero en algunos casos, no debería. Por ejemplo, en el caso anterior, parece que un tipo para apoyar el cierre podría verse así: Retiro mi punto original sobre esto. El siguiente realmente necesita ser un tipo de referencia (o: nonecesita ser, pero si es unstruct De todos modos, solo se va a encajonar). Por lo tanto, ignore el siguiente ejemplo de código. Lo dejo solo para proporcionar contexto para las respuestas que específicamente lo mencionan.

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 pregunta tiene sentido? A mi entender, hay dos posibles explicaciones:

mplementar delegados correctamente como tipos de valor habría requerido trabajo / complejidad adicional, ya que admite cosas como cierres quehace modificar valores de variables locales habría requerido tipos de referencia generados por el compilador de todos modos.Hay algunosotr razones por las cuales, bajo el capó, los delegados simplementehipocresí implementarse como tipos de valor.

Al final, no estoy perdiendo el sueño por esto; es algo por lo que he tenido curiosidad por un tiempo.

Actualiza: En respuesta al comentario de Ani, veo por qué laCompilerGenerated type en mi ejemplo anterior también podría ser un tipo de referencia, ya que si un delegado va a comprender un puntero de función y un puntero de objeto, de todos modos necesitará un tipo de referencia (al menos para funciones anónimas que usan cierres, ya que incluso si usted introdujo un parámetro de tipo genérico adicional, por ejemplo,Action<TCaller> —Esto no cubriría tipos que no se pueden nombrar!). @Sin embarg, ¡todo esto hace que me arrepienta de traer a la discusión la cuestión de los tipos generados por el compilador para los cierres! Mi pregunta principal es sobre delegados, es decir, la cosaco el puntero de función y el puntero de objeto. Todavía me parecees podría ser un tipo de valor.

n otras palabras, incluso si esto ...

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

... requiere la creación deun objeto de tipo de referencia (para apoyar el cierre y darle al delegado algo para referenciar), ¿por qué requiere la creación dedo (el objeto de soporte de cierremá elAction delegado)

* Sí, sí, detalles de implementación, ¡lo sé! Todo lo que realmente quiero decir es almacenamiento de memoria a corto plazo.

Respuestas a la pregunta(7)

Su respuesta a la pregunta