Los delegados de C # son inmutables, pero ¿por qué eso importa?

8

Esta es una pregunta de seguimiento a esta otra pregunta.

Antecedentes

Trabajando desde el Tutorial de delegados de MSDN (C #) , veo lo siguiente:

Tenga en cuenta que una vez que se crea un delegado, el método con el que está asociado nunca cambia: los objetos delegados son inmutables.

Y luego, en el ejemplo de código, veo esto (Extender un poco a través del código):

public delegate void ProcessBookDelegate(Book book);

bookDB.ProcessPaperbackBooks(new ProcessBookDelegate(PrintTitle));
bookDB.ProcessPaperbackBooks(new ProcessBookDelegate(totaller.AddBookToTotal));

La pregunta

Ahora, obviamente, la forma en que se escribe el código aquí se crea un nuevo delegado para cada proceso. Estoy adivinando la inmutabilidad es relevante si se trata de hacer cosas por el estilo

ProcessBookDelegate thisIsATest = new ProcessBookDelegate(PrintTitle);
ProcessBookDelegate thisIsATest = new ProcessBookDelegate(totaller.AddBookToTotal);

que debería ... aún compilar cuando thisIsATestes inmutable? Entonces, ¿qué problema ha resuelto Microsoft haciéndolo inmutable? ¿Qué problemas tendríamos si C # 6 hiciera mutables a los delegados?

EDITAR

Creo que la inmutabilidad evitará esto:

ProcessBookDelegate thisIsATest = new ProcessBookDelegate(PrintTitle);

thisIsATest.ChangeTheDelegateInABadFashion();

someFunction(thisIsATest); //This function works as normal
                           //because the delegate is unchanged
                           //due to immutability

pero la inmutabilidad NO evitará esto:

ProcessBookDelegate thisIsATest = new ProcessBookDelegate(PrintTitle);

thisIsATest = new ProcessBookDelegate(ChangeTheDelegateInABadFashion());

someFunction(thisIsATest); //This function works weird now because
                           //it expected the unchanged delegate

¿Estoy en lo correcto en este entendimiento?

medivh
fuente

Respuestas:

15

Creo que estás confundiendo lo que significa inmutabilidad.

Lo que no es la inmutabilidad

Echale un vistazo a éste ejemplo:

string s = "Hello";
s = "World";

¿Son stringinmutables? Si.
¿Esto compilará? Si.
¿Hemos cambiado de alguna manera la instancia de cadena? No se .

No estamos haciendo ningún cambio a "Hello". Estamos creando una cadena, asignándola a la variable s, luego estamos creando una nueva string y sobrescribiendo la variable scon esta nueva string. La inmutabilidad no juega un papel aquí.

Qué es la inmutabilidad

Si tuviéramos que intentar algo como esto:

s.MutateInSomeWay();

donde MutateInSomeWayes un método que cambia la cadena "Hello"(el valor de s) en sí mismo, que no sería válido debido a la inmutabilidad de la cadena.

Cualquier método stringque lo cambie de alguna manera no lo está cambiando realmente, están creando uno nuevo stringcon el valor cambiado y devolviéndolo a la persona que llama.

pero la inmutabilidad NO evitará esto:

ProcessBookDelegate thisIsATest = new ProcessBookDelegate(PrintTitle);  
thisIsATest = new ProcessBookDelegate(ChangeTheDelegateInABadFashion());
someFunction(thisIsATest); //This function works weird now because
//it expected the unchanged delegate

¿Estoy en lo correcto en este entendimiento?

Sí, la inmutabilidad no lo impedirá porque es perfectamente válido, no ha mutado ningún objeto. Por supuesto, no tiene del todo claro qué es ChangeTheDelegateInABadFashion(), por lo que es difícil ser específico.

Rotem
fuente
1
Vale la pena señalar que el único aspecto de un delegado que es inmutable es el método que se invocará y la identidad de su objetivo. El objeto objetivo en sí será a menudo mutable (de hecho, ¡el propósito de muchos delegados es mutar el objeto objetivo!), E incluso si el delegado tiene la única referencia a un objeto objetivo, sería completamente legítimo Func<int>que devuelva 1 el primero vez que se llama, 2 el segundo, etc., esto se comporta esencialmente como un objeto mutable.
supercat
La inmutabilidad entra cuando intentas modificar una cadena como esta. s[2] = 't'.
M.kazem Akhgary
Como alguien que solo está recogiendo C #, me gustaría poder compartir la respuesta de @ supercat aquí directamente con otros. Para mí no tiene sentido llamar a los delegados inmutables dado el hecho de que pueden contener código arbitrario que puede tener las mutaciones que quiera, visibles desde fuera del delegado.
trptcolin
2

seguridad estándar del hilo, creo; si sabe que un delegado nunca cambiará, puede hacer algunas suposiciones al respecto

en particular, puede estar seguro de que un código malvado no puede cambiar al delegado que está pasando a varias funciones (incluso por accidente)

esto podría causar errores difíciles de rastrear y dejar al programador preguntándose por qué no se llamó a su delegado

monstruo de trinquete
fuente
Dado que los delegados pueden estar vinculados a objetos mutables, no hay razón para esperar que las llamadas repetidas al mismo delegado hagan lo mismo. Sin embargo, tiene razón sobre el punto de seguridad de subprocesos: el propósito de hacer que los delegados sean inmutables es garantizar que siempre estén vinculados a un método que sea compatible con el tipo de instancia adjunta. Si el método y la instancia pudieran cambiar por separado, podrían ocurrir cosas malas.
supercat
2

La respuesta simple es mutar a un delegado tiene muy poco sentido. Un delegado es en gran medida una referencia a una función, un conjunto de código de ejecución. Mutar eso sugiere que estás mutando esa función.

Pregúntese, ¿cómo sería ese método en un delegado y qué haría? En cierto sentido, sería reescribir el código de esa función. Eso abre una enorme (casi intratable) complejidad a la especificación de C # y a las implementaciones del compilador para obtener muy pocos beneficios con grandes costos de rendimiento. De ahí la restricción.

Nathan Dykman
fuente