web-dev-qa-db-fra.com

La méthode impure est appelée pour le champ en lecture seule

J'utilise Visual Studio 2010 + Resharper et il affiche un avertissement sur le code suivant:

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

rect est un readonly Rectangle field, et Resharper me montre cet avertissement:

"La méthode impure est appelée pour un champ en lecture seule de type valeur."

Quelles sont les méthodes impures et pourquoi cet avertissement m'est-il montré?

80
Acidic

Tout d'abord, les réponses de Jon, Michael et Jared sont essentiellement correctes, mais j'ai encore quelques choses à ajouter.

Qu'entend-on par une méthode "impure"?

Il est plus facile de caractériser des méthodes pures. Une méthode "pure" présente les caractéristiques suivantes:

  • Sa sortie est entièrement déterminée par son entrée; sa sortie ne dépend pas d'externalités comme l'heure du jour ou les bits de votre disque dur. Sa sortie ne dépend pas de son histoire; appeler la méthode avec un argument donné deux fois devrait donner le même résultat.
  • Une méthode pure ne produit aucune mutation observable dans le monde qui l'entoure. Une méthode pure peut choisir de muter l'état privé pour des raisons d'efficacité, mais une méthode pure ne mute pas, disons, un champ de son argument.

Par exemple, Math.Cos est une méthode pure. Sa sortie dépend uniquement de son entrée, et l'entrée n'est pas modifiée par l'appel.

Une méthode impure est une méthode qui n'est pas pure.

Quels sont les dangers de passer des structures en lecture seule à des méthodes impures?

Il y en a deux qui me viennent à l'esprit. Le premier est celui souligné par Jon, Michael et Jared, et c'est celui dont Resharper vous met en garde. Lorsque vous appelez une méthode sur une structure, nous transmettons toujours une référence à la variable qui est le récepteur, au cas où la méthode souhaite muter la variable.

Et si vous appelez une telle méthode sur une valeur plutôt que sur une variable? Dans ce cas, nous créons une variable temporaire, y copions la valeur et passons une référence à la variable.

Une variable en lecture seule est considérée comme une valeur, car elle ne peut pas être mutée en dehors du constructeur. Nous copions donc la variable dans une autre variable, et la méthode impure est peut-être en train de muter la copie, lorsque vous avez l'intention de muter la variable.

C'est le danger de passer une structure en lecture seule comme récepteur. Il existe également un danger de passer une structure qui contient un champ en lecture seule. Une structure qui contient un champ en lecture seule est une pratique courante, mais il s'agit essentiellement de rédiger un chèque que le système de type n'a pas les fonds pour encaisser; la "lecture seule" d'une variable particulière est déterminée par le propriétaire du stockage. Une instance d'un type de référence "possède" son propre stockage, mais pas une instance d'un type de valeur!

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

On pense que this.x ne va pas changer car x est un champ en lecture seule et Badness n'est pas un constructeur. Mais...

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

... démontre clairement la fausseté de cela. this et s font référence à la même variable, et que la variable n'est pas en lecture seule!

95
Eric Lippert

Une méthode impure est celle qui n'est pas garantie de laisser la valeur telle qu'elle était.

Dans .NET 4, vous pouvez décorer des méthodes et des types avec [Pure] pour les déclarer purs, et R # en tiendra compte. Malheureusement, vous ne pouvez pas l'appliquer aux membres de quelqu'un d'autre, et vous ne pouvez pas convaincre R # qu'un type/membre est pur dans un projet .NET 3.5 pour autant que je sache. (Cela me mord dans Noda Time tout le temps.)

L'idée est que si vous appelez une méthode qui mute une variable, mais que vous l'appelez sur un champ en lecture seule, c'est probablement ne pas faire ce que vous voulez, donc R # vous en avertira. Par exemple:

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

Ce serait un avertissement vraiment utile si chaque méthode qui était réellement pure était déclarée de cette façon. Malheureusement, ce n'est pas le cas, il y a donc beaucoup de faux positifs :(

50
Jon Skeet

La réponse courte est qu'il s'agit d'un faux positif, et vous pouvez ignorer l'avertissement en toute sécurité.

La réponse la plus longue est que l'accès à un type de valeur en lecture seule crée une copie de celui-ci, de sorte que toute modification de la valeur effectuée par une méthode n'affecterait que la copie. ReSharper ne se rend pas compte que Contains est une méthode pure (ce qui signifie qu'elle n'a pas d'effets secondaires). Eric Lippert en parle ici: Muting Readonly Structs

15
Michael Liu

Il semble que Reshaprer pense que la méthode Contains peut muter la valeur rect. Parce que rect est un readonly struct le compilateur C # crée des copies défensives de la valeur pour empêcher la méthode de muter un champ readonly. Essentiellement, le code final ressemble à ceci

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

Resharper vous avertit ici que Contains peut muter rect d'une manière qui serait immédiatement perdue car elle s'est produite temporairement.

11
JaredPar

Une méthode impure est une méthode qui pourrait avoir des effets secondaires. Dans ce cas, Resharper semble penser qu'il pourrait changer rect. Ce n'est probablement pas le cas, mais la chaîne de preuves est rompue.

5
Henk Holterman