La méthode impure est appelée pour le champ readonly

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

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

rect est un champ readonly Rectangle , et Resharper affiche cet avertissement:

“La méthode Impure est appelée pour un champ de type de valeur en lecture seule.”

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

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

Que signifie une méthode “impure”?

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

  • Sa sortie est entièrement déterminée par ses entrées; 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 un état privé pour des raisons d’efficacité, mais une méthode pure ne modifie pas, par exemple, un domaine de son argument.

Par exemple, Math.Cos est une méthode pure. Sa sortie ne dépend que 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 transmettre des structures en lecture seule à des méthodes impures?

Il y en a deux qui me viennent à l’esprit. Le premier est celui signalé par Jon, Michael et Jared, et celui-ci vous prévient. 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 souhaiterait muter la variable.

Alors, que faire si vous appelez une telle méthode sur une valeur plutôt qu’une variable? Dans ce cas, nous créons une variable temporaire, y copions la valeur et transmettons 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 peut éventuellement modifier la copie lorsque vous souhaitez la modifier.

C’est le danger de transmettre une structure en lecture seule en tant que récepteur . Il existe également un risque de transmission d’une structure contenant un champ en lecture seule. Une structure contenant un champ en lecture seule est une pratique courante, mais elle consiste essentiellement à vérifier que le système de types n’a pas les fonds à encaisser; la “lecture seule” d’une variable particulière est déterminée par le propriétaire du stockage. Une instance d’un type référence “possède” son propre stockage, mais pas une instance d’un type 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 que 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 rapportent à la même variable, et cette variable n’est pas en lecture seule!

Une méthode impure est une méthode qui ne garantit pas 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 pique tout le temps à Noda Time .)

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, ce n’est probablement pas 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 très utile si toutes les méthodes qui étaient réellement pures ont été déclarées ainsi. Malheureusement, ils ne sont pas, donc il y a beaucoup de faux positifs 🙁

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

La réponse la plus longue est que l’access à un type de valeur en lecture seule en crée une copie , de sorte que toute modification apscope à la valeur par une méthode n’affecte que la copie. ReSharper ne réalise pas que Contains est une méthode pure (ce qui signifie qu’elle n’a pas d’effets secondaires). Eric Lippert en parle ici: Mutating Readonly Structs

Il semble que Reshaprer pense que la méthode Contains peut modifier la valeur rect . Comme rect est une readonly struct le compilateur C # crée des copies défensives de la valeur pour empêcher la méthode de muter un champ en 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.

Une méthode impure est une méthode qui pourrait avoir des effets secondaires. Dans ce cas, Resharper semble penser que cela pourrait changer de rect . Ce n’est probablement pas le cas, mais la chaîne de preuves est brisée.