Pregunta El método impuro se llama para el campo de solo lectura


Estoy usando Visual Studio 2010 + Resharper y muestra una advertencia en el siguiente código:

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

rect es un readonly Rectangle campo, y Resharper me muestra esta advertencia:

"El método Impure se llama para el campo de solo lectura del tipo de valor".

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


75
2018-03-29 14:29


origen


Respuestas:


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

¿Qué se entiende por un método "impuro"?

Es más fácil caracterizar 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 llamas a dicho método por 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 de solo lectura como una 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);   
  }
}

Uno piensa que this.x no va a cambiar porque x es un campo de solo 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 refiérase a la misma variable, y ese variable no es solo de lectura!


85
2018-03-29 15:32



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

En .NET 4 puede decorar métodos y tipos con [Pure] declararlos puros, y R # tomará nota 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 Hora de Noda todo el tiempo.)

los idea es que si llamas a un método que muta una variable, pero lo llamas en un campo de solo 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
    }
}

Esta sería una advertencia muy útil si cada método que en realidad era puro se declarara de esa manera. Desafortunadamente no lo son, entonces hay muchos falsos positivos :(


46
2018-03-29 14:32



La respuesta corta es que este es un falso positivo, y puede ignorar la advertencia de forma segura.

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


14
2018-03-29 14:36



Parece que Reshaprer cree que el método Contains puede mutar el rect valor. Porque rect es un readonly struct el compilador de C # hace copias defensivas del valor para evitar que el método mute una readonly campo. Esencialmente, el código final se ve así

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

Resharper te advierte que Contains puede mutar rect de una manera que se perdería de inmediato porque sucedió de forma temporal.


11
2018-03-29 14:35



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, pero la cadena de evidencia está rota.


5
2018-03-29 14:33