Imutabilidade de estruturas

Duplicar Possível:
Por que as estruturas mutáveis ​​são más?

Eu li em muitos lugares incluindo aqui que é melhor tornar as estruturas imutáveis.

Qual é a razão por trás disso? Eu vejo muitas estruturas criadas pela Microsoft que são mutáveis, como as do xna. Provavelmente existem muitos mais no BCL.

Quais são os prós e contras de não seguir esta diretriz?

As estruturas devem representar valores . Os valores não mudam. O número 12 é eterno.

No entanto, considere:

Foo foo = new Foo(); // a mutable struct foo.Bar = 27; Foo foo2 = foo; foo2.Bar = 55; 

Agora foo.Bar e foo2.Bar é diferente, o que geralmente é inesperado. Especialmente nos cenários como propriedades (felizmente o compilador detecta isso). Mas também collections etc; como você os mexe sensatamente?

A perda de dados é muito fácil com estruturas mutáveis.

A grande desvantagem é que as coisas não se comportam como você espera – particularmente se a mutabilidade vem de uma mistura do valor direto e um tipo de referência dentro dela.

Para ser honesto, não consigo me lembrar de todos os problemas estranhos que vi pessoas surgirem em grupos de notícias quando usaram estruturas mutáveis ​​- mas essas razões certamente existem. Estruturas mutáveis ​​causam problemas. Ficar longe.

EDIT: Acabei de encontrar um email que escrevi um tempo atrás sobre este tópico. Elabora só um pouquinho:

  • Está filosoficamente errado: uma estrutura deve representar algum tipo de valor fundamental. Esses são basicamente imutáveis. Você não pode alterar o número 5. Você pode alterar o valor de uma variável de 5 para 6, mas não faz uma alteração lógica no próprio valor.

  • É praticamente um problema: cria muitas situações estranhas. É particularmente ruim se for mutável através de uma interface. Então você pode começar a alterar os valores da checkbox. Ick. Eu vi um monte de posts de newsgroups que são devido a pessoas tentando usar estruturas mutáveis ​​e se deparando com problemas. Eu vi um exemplo de LINQ muito estranho que estava falhando porque List.Enumerator é uma estrutura, por exemplo.

Eu uso estruturas mutáveis ​​com frequência em meu projeto (crítico de desempenho) – e não me deparo com problemas, porque entendo as implicações da semântica da cópia. Tanto quanto eu posso dizer, a principal razão pela qual as pessoas defendem as estruturas imutáveis ​​é para que as pessoas que não entendem as implicações não possam se meter em problemas.

Isso não é uma coisa tão terrível – mas estamos em perigo agora de se tornar “a verdade do evangelho”, quando na verdade há momentos em que é legitimamente a melhor opção para fazer uma estrutura mutável. Como com todas as coisas, há exceções à regra.

Não há nada mais barato para manipular do que uma estrutura mutável, e é por isso que você geralmente a vê em código de alto desempenho, como as rotinas de processamento de charts.

Infelizmente as estruturas mutáveis ​​não funcionam bem com objects e propriedades, é muito fácil modificar uma cópia de uma estrutura em vez da própria estrutura. Assim, eles não são apropriados para a maior parte do seu código.

PS Para evitar o custo de copiar estruturas mutáveis, elas geralmente são armazenadas e passadas em matrizes.

A razão técnica é que as estruturas mutáveis parecem ser capazes de fazer coisas que não fazem. Como as semânticas de tempo de design são as mesmas dos tipos de referência, isso se torna confuso para os desenvolvedores. Este código:

 public void DoSomething(MySomething something) { something.Property = 10; } 

Comporta-se de forma bem diferente, dependendo de se MySomething é uma struct ou uma class . Para mim, esse é um motivo convincente, mas não o mais convincente. Se você olhar para o Value Object do DDD , poderá ver a conexão com o modo como as estruturas devem ser tratadas. Um object de valor no DDD pode ser melhor representado como um tipo de valor em .Net (e, portanto, uma estrutura). Porque não tem identidade, não pode mudar.

Pense nisso em termos de algo como o seu endereço. Você pode “alterar” seu endereço, mas o endereço em si não mudou. Na verdade, você tem um novo endereço atribuído a você. Conceitualmente, isso funciona, porque se você realmente mudasse seu endereço, seus colegas de quarto teriam que se mudar também.

Você pediu os prós e contras de não seguir a diretriz de que as estruturas deveriam ser imutáveis.

Contras: Os contras são bem abordados nas respostas existentes, e a maioria dos problemas descritos se deve à mesma causa – comportamento inesperado devido à semântica do valor das estruturas .

Prós: O principal pro de usar estruturas mutáveis ​​pode ser o desempenho . Obviamente, este conselho vem com todas as advertências habituais sobre otimizações: certifique-se de que parte do seu código precisa ser otimizada e certifique-se de que quaisquer mudanças realmente otimizem o desempenho do seu código via perfilamento.

Para um excelente artigo discutindo quando você pode querer usar estruturas mutáveis, veja o Performance Quiz de Rico Mariani sobre Programação Baseada em Valor (ou mais especificamente, as respostas ).

Uma estrutura geralmente deve representar uma unidade única de algum tipo. Como tal, não faz muito sentido alterar uma das propriedades do valor, faz mais sentido criar um valor completamente novo se você quiser um valor diferente de alguma forma.

A semântica fica mais simples ao usar estruturas imutáveis, e você evita armadilhas como esta:

 // a struct struct Interval { int From { get; set; } int To { get; set; } } // create a list of structs List intervals = new List(); // add a struct to the list intervals.Add(new Interval()); // try to set the values of the struct intervals[0].From = 10; intervals[0].To = 20; 

O resultado é que a estrutura na lista não é alterada. A expressão Interval [0] copia o valor da estrutura da lista e, em seguida, você altera a propriedade do valor temporário, mas o valor nunca é colocado de volta na lista.

Edit: Alterou o exemplo para usar uma lista em vez de uma matriz.

Quando você copiar estruturas em torno de você copiar seu conteúdo, portanto, se você modificar uma versão copiada, o “original” não será atualizado.

Esta é uma fonte de erros, pois mesmo que você saiba que você cai na armadilha de copiar uma estrutura (apenas passando-a para um método) e modificando a cópia.

Apenas aconteceu comigo na semana passada, me manteve uma hora procurando por um bug.

Manter estruturas imutáveis ​​impede que …

Além disso, você precisa ter certeza de que tem uma boa razão para usar as estruturas – “otimização” ou “eu quero algo que aloque rapidamente na pilha” não conta como uma resposta. Marshaling ou coisas em que você depende do layout – ok, mas você normalmente não deve manter essas structs por muito tempo, elas são valores e não objects.

a razão pela qual você deve fazer structs imutáveis ​​é que eles são ValueTypes , o que significa que eles são copiados toda vez que você os passa para um método.

Portanto, se, por exemplo, você tivesse uma propriedade que retornasse uma estrutura, modificar o valor de um campo nessa estrutura seria inútil, porque o getter retornaria uma cópia da estrutura, em vez de uma referência à estrutura. Eu vi isso feito em código, e muitas vezes é difícil de pegar.

Se você projetar suas estruturas para imutabilidade, ajudará o programador a evitar esses erros.