2012-03-29 11 views
71

Estoy usando Visual Studio 2010 + ReSharper y muestra una advertencia en el siguiente código:método impuro se llama campo de sólo lectura

if (rect.Contains(point)) 
{ 
    ... 
} 

rect es un campo readonly Rectangle, y me demuestra ReSharper esta advertencia:

"Se requiere el método Impuro para el campo de solo lectura del tipo de valor".

¿Qué son los métodos impuros y por qué se me muestra esta advertencia?

+0

http://www.minddriven.de/index.php/technology/dot-net/code-contracts/code-contracts-method-purity – siride

Respuesta

81

En primer lugar, las respuestas de Jon, Michael y Jared son esencialmente correctas, pero tengo algunas cosas más que me gustaría añadirles.

¿Qué significa un método "impuro"?

Es más fácil caracterizar los métodos puros. Un método "puro" tiene las siguientes características:

  • Su salida está completamente determinada por su entrada; su salida no depende de externalidades como la hora del día o los bits en su disco duro. Su salida no depende de su historia; llamar al método con un argumento dado dos veces debería dar el mismo resultado.
  • Un método puro no produce mutaciones observables en el mundo que lo rodea. Un método puro puede elegir mutar el estado privado por eficiencia, pero un método puro no cambia, digamos, un campo de su argumento.

Por ejemplo, Math.Cos es un método puro. Su salida depende solo de su entrada, y la entrada no cambia por la llamada.

Un método impuro es un método que no es puro.

¿Cuáles son algunos de los peligros de pasar estructuras de solo lectura a métodos impuros?

Hay dos que vienen a la mente. El primero es el señalado por Jon, Michael y Jared, y este es el que Resharper te está advirtiendo. Cuando llamamos a un método en una estructura, siempre pasamos una referencia a la variable que es el receptor, en caso de que el método desee mutar la variable.

Entonces, ¿qué ocurre si llama a tal método en un valor, en lugar de una variable? En ese caso, hacemos una variable temporal, copiamos el valor en ella y pasamos una referencia a la variable.

Una variable de solo lectura se considera un valor, porque no se puede mutar fuera del constructor. Así que estamos copiando la variable a otra variable, y el método impuro posiblemente está mutando la copia, cuando pretendes mutar la variable.

Ese es el peligro de pasar una estructura readonly como un receptor . También existe el peligro de pasar una estructura que contenga un campo de solo lectura. Una estructura que contiene un campo de solo lectura es una práctica común, pero básicamente está escribiendo una verificación de que el sistema de tipo no tiene los fondos para cobrar; el propietario del almacenamiento determina la "capacidad de solo lectura" de una variable en particular. Una instancia de un tipo de referencia "posee" su propio almacenamiento, pero una instancia de un tipo de valor no lo hace.

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); 
    } 
} 

Se cree que this.x no va a cambiar porque x es un campo de sólo lectura y Badness no es un constructor. Pero ...

S s = new S(1); 
s.Badness(ref s); 

... demuestra claramente la falsedad de eso. this y s se refieren a la misma variable, y esa variable no es de solo lectura.

+0

Bastante justo, pero tenga en cuenta este código: 'struct Id {' 'private readonly int _id;' 'public Id (int id) {_id = id; } ' ' public int ToInt() => _id; ' '} ' ¿Por qué * ToInt * impure? – boskicthebrain

+0

@boskicthebrain: ¿Su pregunta es realmente "por qué Resharper considera que esto es impuro?" Si esa es su pregunta, busque a alguien que trabaje en R # y ¡pregúnteles! –

+0

Resharper dará esta advertencia incluso si el método es nulo y no hace nada excepto 'return'. Basado en eso, supongo que el único criterio es si el método tiene el atributo '[Pure]' o no. – bornfromanegg

46

Un método impuro es uno que no se garantiza que deje el valor tal como estaba.

En .NET 4 puede decodificar métodos y tipos con [Pure] para declarar que son puros, y R # se dará cuenta de esto. Desafortunadamente, no se puede aplicar a los miembros de otra persona, y no se puede convencer a R # de que un tipo/miembro es puro en un proyecto .NET 3.5 por lo que sé. (Esto me muerde en Noda Time todo el tiempo.)

La idea es que si usted está llamando a un método que muta una variable, pero llamarlo en un campo de sólo lectura, es probable que no haciendo lo que quieras, entonces R # te advertirá sobre esto. Por ejemplo:

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 
    } 
} 

Ésta sería una advertencia muy útil si todos los métodos que en realidad era pura fue declarado de esa manera. Desafortunadamente no lo son, entonces hay muchos falsos positivos :(

+0

Eso significa que un método impuro puede cambiar los campos subyacentes del mutable valuetype pasado a él? – Acidic

+0

@Acidic: No es el valor del argumento, incluso un método impuro puede hacer eso, sino el valor que * se le asigna *. (Vea mi ejemplo, donde el método ni siquiera tiene parámetros). –

+0

¿Eso significa que cualquier tipo de valor que 'readonly' se vuelve inmutable? – Acidic

5

Un método Impure es un método que podría tener efectos secundarios. En este caso, Resharper parece pensar que podría cambiar rect. Probablemente no lo haga. t, pero la cadena de pruebas se rompe.

11

suena como Reshaprer cree que el método Contains puede mutar el valor rect. Debido rect es una readonly struct el compilador de C# hace copias defensivas del valor para evitar que el método de mutar un readonly En esencia, el código final se ve así

Rectangle temp = rect; 
if (temp.Contains(point)) { 
    ... 
} 

Resharper le advierte que Contains puede mutar rect de una manera que se perderá de inmediato porque ocurrió de forma temporal.

+0

Entonces, eso no afectaría ninguna lógica realizada en el método, solo evitaría que mute el valor al que fue invocado, ¿verdad? – Acidic

+1

@Acidic que es correcto – JaredPar

13

La respuesta breve es que se trata de un falso positivo, y puede ignorar la advertencia de forma segura.

La respuesta más larga es que al acceder a un tipo de valor de solo lectura se crea una copia , de modo que cualquier cambio en el valor realizado por un método solo afectaría a la copia. ReSharper no se da cuenta de que Contains es un método puro (lo que significa que no tiene efectos secundarios). Eric Lippert habla de ello aquí: Mutating Readonly Structs

+1

Por favor, nunca ignore esta advertencia hasta que se entienda completamente !!! Un buen ejemplo de que esto puede protegerlo es este constructo: 'private readinly SpinLock _spinLock = new SpinLock();' - un bloqueo así sería completamente inútil (ya que el modificador readonly hace que se cree una copia sobre la marcha cada vez) método es llamado) – Jan

Cuestiones relacionadas