Estou usando o Visual Studio 2010 + Resharper e ele mostra um aviso no seguinte código:
if (rect.Contains(point))
{
...
}
rect
é um readonly Rectangle
campo, e Resharper me mostra este aviso:
"Método impuro é chamado para campo somente leitura do tipo de valor."
O que são métodos impuros e por que este aviso está sendo mostrado para mim?
Respostas:
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.
É mais fácil caracterizar métodos puros. Um método "puro" tem as seguintes características:
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.
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); // This should be the same, right? Console.WriteLine(this.x); } }
Pode-se pensar que isso
this.x
não vai mudar porque x é um campo somente leitura eBadness
não é um construtor. Mas...S s = new S(1); s.Badness(ref s);
... demonstra claramente a falsidade disso.
this
e ses
referem à mesma variável, e essa variável não é somente leitura!fonte
struct Id {
private readonly int _id;
public Id(int id) { _id = id; }
public int ToInt() => _id;
}
Por que o ToInt é impuro?return
. Com base nisso, estou supondo que o único critério é se o método tem ou não o[Pure]
atributo.rect
. Estamos dizendo que uma cópia derect
é passada para oContains
método?Um método impuro é aquele que não tem garantia de deixar o valor como estava.
No .NET 4, você pode decorar métodos e tipos com
[Pure]
para declará-los puros, e R # vai notar isso. Infelizmente, você não pode aplicá-lo aos membros de outra pessoa e não pode convencer R # de que um tipo / membro é puro em um projeto .NET 3.5, tanto quanto eu sei. (Isso me incomoda o tempo todo no Tempo Noda .)A ideia é que, se você estiver chamando um método que transforma uma variável, mas o chama em um campo somente leitura, ele provavelmente não está fazendo o que você deseja, então R # irá avisá-lo sobre isso. Por exemplo:
public struct Nasty { public int value; public void SetValue() { value = 10; } } class Test { static readonly Nasty first; static Nasty second; static void Main() { first.SetValue(); second.SetValue(); Console.WriteLine(first.value); // 0 Console.WriteLine(second.value); // 10 } }
Este seria um aviso muito útil se todos os métodos realmente puros fossem declarados dessa forma. Infelizmente não são, portanto, há muitos falsos positivos :(
fonte
JetBrains.Annotations.PureAttribute
vez deSystem.Diagnostics.Contracts.PureAttribute
, eles têm o mesmo significado para a análise de código ReSharper e devem funcionar igualmente no .NET 3.5, .NET 4 ou Silverlight. Você também pode anotar externamente assemblies que você não possui usando arquivos XML (dê uma olhada no diretório ExternalAnnotations no caminho do bin ReSharper), pode realmente ser muito útil!System.Diagnostics.Contracts.PureAttribute
esse aviso não suprimia em R # 8.2, enquanto oJetBrains.Annotations.PureAttribute
fazia. Os dois atributos também têm descrições diferentes: oPure
atributo de contratos implica "o resultado depende apenas de parâmetros", enquanto o JetBrainsPure
implica "não causa mudanças de estado visível" sem excluir o estado do objeto que está sendo usado para calcular o resultado. (Mas ainda assim os contratos quePure
não têm o mesmo efeito sobre este aviso provavelmente são um bug.)A resposta curta é que esse é um falso positivo e você pode ignorar o aviso com segurança.
A resposta mais longa é que acessar um tipo de valor somente leitura cria uma cópia dele, de forma que quaisquer alterações no valor feitas por um método afetariam apenas a cópia. O ReSharper não percebe que
Contains
é um método puro (o que significa que não tem efeitos colaterais). Eric Lippert fala sobre isso aqui: Mutating Readonly Structsfonte
private readonly SpinLock _spinLock = new SpinLock();
- tal bloqueio seria completamente inútil (uma vez que o modificador somente leitura faz com que uma cópia instantânea seja criada cada vez que o método Enter é chamado)Parece que Reshaprer acredita que o método
Contains
pode alterar orect
valor. Porrect
ser um,readonly struct
o compilador C # faz cópias defensivas do valor para evitar que o método modifique umreadonly
campo. Essencialmente, o código final se parece com issoRectangle temp = rect; if (temp.Contains(point)) { ... }
Resharper está avisando você aqui que
Contains
pode sofrer mutaçãorect
de uma forma que seria imediatamente perdida porque aconteceu temporariamente.fonte
Um método impuro é um método que pode ter efeitos colaterais. Nesse caso, Resharper parece pensar que isso pode mudar
rect
. Provavelmente não, mas a cadeia de evidências está quebrada.fonte