Je voudrais développer un point précis qu'Eric Lippert a soulevé dans sa réponse et mettre en lumière une occasion particulière qui n'a été du tout évoquée par personne d'autre. Eric a dit:
[...] une affectation laisse presque toujours la valeur qui vient d'être affectée dans un registre.
Je voudrais dire que l'affectation laissera toujours derrière la valeur que nous avons essayé d'attribuer à notre opérande gauche. Pas seulement «presque toujours». Mais je ne sais pas car je n'ai pas trouvé ce problème commenté dans la documentation. Cela pourrait théoriquement être une procédure mise en œuvre très efficace pour «laisser derrière» et ne pas réévaluer l'opérande gauche, mais est-ce efficace?
«Efficace» oui pour tous les exemples construits jusqu'à présent dans les réponses de ce fil. Mais efficace dans le cas des propriétés et des indexeurs qui utilisent des accesseurs get et set? Pas du tout. Considérez ce code:
class Test
{
public bool MyProperty { get { return true; } set { ; } }
}
Ici, nous avons une propriété, qui n'est même pas un wrapper pour une variable privée. Chaque fois qu'il sera appelé, il retournera vrai, chaque fois que l'on essayera de fixer sa valeur, il ne fera rien. Ainsi, chaque fois que cette propriété est évaluée, il sera véridique. Voyons ce qui se passe:
Test test = new Test();
if ((test.MyProperty = false) == true)
Console.WriteLine("Please print this text.");
else
Console.WriteLine("Unexpected!!");
Devinez ce qu'il imprime? Il imprime Unexpected!!
. En fait, l'accesseur set est en effet appelé, ce qui ne fait rien. Mais par la suite, l'accesseur get n'est jamais appelé du tout. L'affectation laisse simplement derrière la false
valeur que nous avons essayé d'attribuer à notre propriété. Et cette false
valeur est ce que l'instruction if évalue.
Je terminerai par un exemple concret qui m'a amené à faire des recherches sur ce problème. J'ai créé un indexeur qui était un wrapper pratique pour une collection ( List<string>
) qu'une de mes classes avait comme variable privée.
Le paramètre envoyé à l'indexeur était une chaîne, qui devait être traitée comme une valeur dans ma collection. L'accesseur get renverrait simplement true ou false si cette valeur existait ou non dans la liste. Ainsi, l'accesseur get était une autre façon d'utiliser la List<T>.Contains
méthode.
Si l'accesseur set de l'indexeur était appelé avec une chaîne comme argument et que l'opérande de droite était un booléen true
, il ajouterait ce paramètre à la liste. Mais si le même paramètre était envoyé à l'accesseur et que l'opérande de droite était un booléen false
, il supprimerait à la place l'élément de la liste. Ainsi, l'accesseur set a été utilisé comme une alternative pratique à la fois List<T>.Add
et List<T>.Remove
.
Je pensais avoir une "API" soignée et compacte enveloppant la liste avec ma propre logique implémentée en tant que passerelle. Avec l'aide d'un indexeur seul, je pouvais faire beaucoup de choses avec quelques combinaisons de touches. Par exemple, comment puis-je essayer d'ajouter une valeur à ma liste et vérifier qu'elle s'y trouve? Je pensais que c'était la seule ligne de code nécessaire:
if (myObject["stringValue"] = true)
; // Set operation succeeded..!
Mais comme mon exemple précédent l'a montré, l'accesseur get qui est censé voir si la valeur est vraiment dans la liste n'a même pas été appelé. La true
valeur était toujours laissée pour détruire efficacement la logique que j'avais implémentée dans mon accesseur get.