Em primeiro lugar, as respostas de Jon, Michael e Jared estão essencialmente corretas, mas tenho mais algumas coisas que gostaria de acrescentar a elas.
O que se entende por método "impuro"?
É mais fácil caracterizar métodos puros. Um método "puro" tem as seguintes características:
- Sua saída é inteiramente determinada por sua entrada; sua saída não depende de externalidades como a hora do dia ou os bits em seu disco rígido. Sua saída não depende de seu histórico; chamar o método com um determinado argumento duas vezes deve dar o mesmo resultado.
- Um método puro não produz mutações observáveis no mundo ao seu redor. Um método puro pode escolher transformar o estado privado por uma questão de eficiência, mas um método puro não muda, digamos, um campo de seu argumento.
Por exemplo, Math.Cos
é um método puro. Sua saída depende apenas de sua entrada, e a entrada não é alterada pela chamada.
Um método impuro é um método que não é puro.
Quais são alguns dos perigos de passar structs somente leitura para métodos impuros?
Existem dois que vêm à mente. O primeiro é aquele apontado por Jon, Michael e Jared, e é sobre este que Resharper está avisando. Quando você chama um método em uma estrutura, sempre passamos uma referência para a variável que é o receptor, caso o método deseje transformar a variável.
E daí se você chamar esse método em um valor, em vez de em uma variável? Nesse caso, criamos uma variável temporária, copiamos o valor para ela e passamos uma referência à variável.
Uma variável somente leitura é considerada um valor, porque não pode sofrer mutação fora do construtor. Portanto, estamos copiando a variável para outra variável, e o método impuro possivelmente está alterando a cópia, quando você pretende que ele altere a variável.
Esse é o perigo de passar uma estrutura somente leitura como um receptor . Também existe o perigo de passar uma estrutura que contém um campo somente leitura. Uma estrutura que contém um campo somente leitura é uma prática comum, mas é essencialmente um cheque de que o sistema de tipos não tem fundos para descontar; o "read-only-ness" de uma determinada variável é determinado pelo proprietário do armazenamento. Uma instância de um tipo de referência "possui" seu próprio armazenamento, mas uma instância de um tipo de valor não!
struct S
{
private readonly int x;
public S(int x) { this.x = x; }
public void Badness(ref S s)
{
Console.WriteLine(this.x);
s = new S(this.x + 1);
Console.WriteLine(this.x);
}
}
Pode-se pensar que isso this.x
não vai mudar porque x é um campo somente leitura e Badness
não é um construtor. Mas...
S s = new S(1);
s.Badness(ref s);
... demonstra claramente a falsidade disso. this
e se s
referem à mesma variável, e essa variável não é somente leitura!