Os delegados em C # são imutáveis ​​- mas por que isso importa?

8

Esta é uma pergunta de acompanhamento para essa outra pergunta.

fundo

Trabalhando no Tutorial de Delegados do MSDN (C #) , vejo o seguinte:

Observe que depois que um delegado é criado, o método ao qual ele está associado nunca muda - os objetos delegados são imutáveis.

E então, no exemplo de código, vejo isso (espalhe um pouco pelo código):

public delegate void ProcessBookDelegate(Book book);

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

A questão

Agora, obviamente, da maneira como o código é escrito aqui, um novo delegado é criado para cada processo. Eu estou supondo que a imutabilidade é relevante se você tentar fazer coisas como

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

que deve ... ainda compilar quando thisIsATesté imutável? Então, que problema a Microsoft resolveu tornando-a imutável? Quais problemas encontraríamos se o C # 6 tornasse os delegados mutáveis?

EDITAR

Acredito que a imutabilidade impedirá isso:

ProcessBookDelegate thisIsATest = new ProcessBookDelegate(PrintTitle);

thisIsATest.ChangeTheDelegateInABadFashion();

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

mas a imutabilidade NÃO impedirá isso:

ProcessBookDelegate thisIsATest = new ProcessBookDelegate(PrintTitle);

thisIsATest = new ProcessBookDelegate(ChangeTheDelegateInABadFashion());

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

Estou correto neste entendimento?

medivh
fonte

Respostas:

15

Eu acho que você está misturando o que significa imutabilidade.

O que a imutabilidade não é

Veja este exemplo:

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

São stringimutáveis? Sim.
Isso irá compilar? Sim.
De alguma forma, alteramos a instância da string? Não .

Não estamos fazendo alterações "Hello". Estamos criando uma string, atribuindo-a à variável s, depois criando uma nova string e substituindo a variável spor essa nova string. A imutabilidade não desempenha um papel aqui.

O que é imutabilidade

Se tentássemos algo assim:

s.MutateInSomeWay();

onde MutateInSomeWayé um método que altera a própria string "Hello"(o valor de s), que não seria válido devido à imutabilidade da string.

Quaisquer métodos stringque o alterem de alguma forma não estejam realmente mudando, eles estão criando um novo stringcom o valor alterado e retornando-o ao chamador.

mas a imutabilidade NÃO impedirá isso:

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

Estou correto neste entendimento?

Sim, a imutabilidade não o impedirá porque é perfeitamente válido, você não alterou nenhum objeto. Obviamente, você não é totalmente claro sobre o que é ChangeTheDelegateInABadFashion(), por isso é difícil ser específico.

Rotem
fonte
1
Vale a pena notar que o único aspecto imutável de um delegado é o método que será invocado e a identidade de seu destino. O objeto de destino em si geralmente é mutável (na verdade, o objetivo de muitos delegados é alterar o objeto de destino!), E mesmo que o delegado possua a única referência a um objeto de destino, seria totalmente legítimo Func<int>que retornasse 1 o primeiro quando é chamado, 2 o segundo, etc. - isso se comporta essencialmente como um objeto mutável.
Supercat
A imutabilidade ocorre quando você tenta modificar uma string como esta. s[2] = 't'.
M.kazem Akhgary
Como alguém que acabou de pegar o C #, gostaria de compartilhar a resposta do @ supercat aqui diretamente com outras pessoas. Não faz sentido chamar imutáveis ​​os delegados, uma vez que eles podem conter código arbitrário que pode ter as mutações que desejar, visíveis de fora do delegado.
trptcolin
2

segurança padrão da linha, acredito; se você souber que um delegado nunca mudará, faça algumas suposições sobre isso

em particular, você pode ter certeza de que um código incorreto não pode alterar o delegado que você está passando para várias funções (mesmo que por acidente)

isso pode dificultar o rastreamento de bugs e deixar o programador se perguntando por que seu delegado não foi chamado

catraca arrepiante
fonte
Como os delegados podem ser vinculados a objetos mutáveis, não há razão para esperar que chamadas repetidas para o mesmo delegado façam a mesma coisa. Você está certo sobre o ponto de segurança do encadeamento: o objetivo de tornar os delegados imutáveis ​​é garantir que eles estejam sempre vinculados a um método compatível com o tipo da instância anexada. Se o método e a instância pudessem mudar separadamente, coisas ruins poderiam acontecer.
Supercat 21/13 /
2

A resposta simples é alterar um delegado, faz muito pouco sentido. Um delegado é muito uma referência a uma função, um conjunto de código em execução. Mutação que sugere que você está mutando essa função.

Pergunte-se, como seria esse método em um delegado e o que faria? Em certo sentido, seria reescrever o código dessa função. Isso abre uma quantidade enorme (quase intratável) de complexidade às implementações de especificação e compilador C # para pouquíssimo benefício com grandes custos de desempenho. Daí a restrição.

Nathan Dykman
fonte