pourquoi C # ne fournit pas d’aide interne pour passer la propriété comme référence?

C’est un problème de LANGUAGE DESIGN.

Ne répondez pas à la question avant d’avoir lu l’intégralité de l’article! Je vous remercie.

Avec toutes les aides existantes en C # (comme lambdas, ou des propriétés automatiques), il est très étrange pour moi que je ne puisse pas transmettre propriété par référence. Disons que j’aimerais faire ça:

foo(ref my_class.prop); 

Je reçois une erreur alors j’écris à la place:

 { var tmp = my_class.prop; foo(tmp); my_class.prop = tmp; } 

Et maintenant ça marche. Mais remarquez deux choses:

  1. c’est un gabarit général, je n’ai pas mis de type nulle part, seulement “var”, donc il s’applique à tous les types et nombre de propriétés que je dois passer

  2. Je dois le faire encore et encore, sans bénéfice – c’est un travail mécanique

Le problème existant tue en réalité des fonctions aussi utiles que l’échange. Swap est normalement long de 3 lignes, mais comme il faut 2 références, l’appel prend 5 lignes. Bien sûr, c’est absurde et j’écris simplement “swap” à la main chaque fois que je voudrais l’appeler. Mais cela montre que C # empêche le code réutilisable, mauvais.

LA QUESTION

Alors, quel mal peut arriver si le compilateur crée automatiquement des variables temporaires (comme je le fais à la main), appelle la fonction et assigne les valeurs aux propriétés? Est-ce que c’est un danger? Je ne le vois pas, alors je suis curieux de savoir à quoi ressemble le design de ce numéro.

À votre santé,

EDIT Comme 280Z28 a donné d’excellents exemples, nous pensons que l’emballage des propriétés avec des variables temporaires serait utile. Peut-être quelque chose comme ça:

 Swap(inout my_class.prop1,inout my_class.prop2); 

Sinon, pas de réel échange pour C # 🙁

Vous pouvez faire beaucoup d’hypothèses sur la signification et le comportement d’un paramètre ref . Par exemple,

Cas 1:

 int x; Interlocked.Increment(ref x); 

Si vous pouviez passer une propriété par référence à cette méthode, l’appel serait le même mais il éliminerait complètement la sémantique de la méthode.

Cas 2:

 void WaitForCompletion(ref bool sortinggger) { while (!sortinggger) Thread.Sleep(1000); } 

Résumé: un paramètre by-ref transmet l’adresse d’un emplacement de mémoire à la fonction. Une implémentation créant une variable temporaire afin de “passer une propriété par référence” équivaudrait sémantiquement à passer par valeur, ce qui est précisément le comportement que vous n’autorisez pas lorsque vous faites du paramètre un paramètre.

Votre proposition s’appelle «copier en copie». La sémantique copie à la copie est légèrement différente de ce que nous pourrions appeler la sémantique “ref to variable”; assez différents pour être déroutant et faux dans de nombreuses situations. D’autres vous ont déjà donné des exemples. il y a beaucoup plus. Par exemple:

 void M() { F(ref this.p); } void F(ref int x) { x = 123; B(); } void B() { Console.WriteLine(this.p); } 

Si “this.p” est une propriété, avec votre proposition, cela affiche l’ ancienne valeur de la propriété. S’il s’agit d’un champ, la nouvelle valeur est imprimée.

Maintenant, imaginez que vous refactoriez un champ pour qu’il devienne une propriété. En langage réel, cela provoque des erreurs si vous passez un champ par réf; le problème est porté à votre attention. Avec votre proposition, il n’y a pas d’erreur. au lieu de cela, le comportement change silencieusement et subtilement. Cela fait des bugs.

La cohérence est importante en C #, en particulier dans certaines parties du langage que les gens trouvent déroutantes, comme la sémantique de référence. Je voudrais que les références soient toujours une copie avec copie ou jamais une copie avec copie. Le faire d’une manière ou d’une autre peut sembler très mauvais en C #, un langage qui privilégie la cohérence par rapport à la brièveté.

Parce qu’une propriété est une méthode. C’est une construction de langage répondant à un modèle d’encapsulation du paramétrage et de la récupération d’un champ privé via un ensemble de méthodes. Il est fonctionnellement équivalent à ceci:

 class Foo { private int _bar; public int GetBar( ) { return _bar; } public void SetBar( ) { _bar = value; } } 

Avec un argument ref, les modifications de la variable sous-jacente seront observées par la méthode, cela n’arrivera pas dans votre cas. En d’autres termes, ce n’est pas exactement la même chose.

 var t = obj.prop; foo(ref t); obj.prop = t; 

Ici, les effets secondaires de getter et de setter ne sont visibles qu’une fois, quel que soit le nombre de fois où le paramètre “by-ref” a été affecté.

Imaginez une propriété calculée dynamicment. Sa valeur peut changer à tout moment. Avec cette construction, foo n’est pas tenu à jour même si le code le suggère (“Je passe la propriété à la méthode”)

Alors, quel mal peut arriver si le compilateur crée automatiquement des variables temporaires (comme je le fais à la main), appelle la fonction et assigne les valeurs aux propriétés? Est-ce que c’est un danger?

Le danger est que le compilateur fait quelque chose que vous ne connaissez pas. Rendre le code déroutant car les propriétés sont des méthodes et non des variables.

Je ne donnerai qu’un exemple simple où cela créerait de la confusion. Supposons que c’était possible (comme dans VB):

 class Weird { public int Prop { get; set; } } static void Test(ref int x) { x = 42; throw new Exception(); } static void Main() { int v = 10; try { Test(ref v); } catch {} Console.WriteLine(v); // prints 42 var c = new Weird(); c.Prop = 10; try { Test(ref c.Prop); } catch {} Console.WriteLine(c.Prop); // prints 10!!! } 

Agréable. N’est-ce pas?

Comme Eric Lippert aime bien le souligner, chaque fonctionnalité linguistique doit être comprise, conçue, spécifiée, mise en œuvre, testée et documentée. Et ce n’est évidemment pas un scénario / sharepoint douleur commun.