Comment atsortingbuer par «référence» un champ de classe en c #?

J’essaie de comprendre comment affecter par “référence” un champ de classe en c #.

J’ai l’exemple suivant à considérer:

public class X { public X() { ssortingng example = "X"; new Y( ref example ); new Z( ref example ); System.Diagnostics.Debug.WriteLine( example ); } } public class Y { public Y( ref ssortingng example ) { example += " (Updated By Y)"; } } public class Z { private ssortingng _Example; public Z( ref ssortingng example ) { this._Example = example; this._Example += " (Updated By Z)"; } } var x = new X(); 

Lorsque vous exécutez le code ci-dessus, le résultat est le suivant:

X (mis à jour par Y)

Et pas:

X (mis à jour par Y) (mis à jour par Z)

Comme je l’avais espéré.

Il semble que l’atsortingbution d’un “paramètre de référence” à un champ perd la référence.

Existe-t-il un moyen de garder la référence lors de l’atsortingbution d’un champ?

Merci.

N ° ref est purement une convention d’appel. Vous ne pouvez pas l’utiliser pour qualifier un champ. Dans Z, _Example est défini sur la valeur de la référence de chaîne transmise. Vous lui affectez ensuite une nouvelle référence de chaîne à l’aide de + =. Vous n’assignez jamais à l’exemple, donc le ref n’a aucun effet.

Le seul moyen de contourner ce que vous voulez est d’avoir un object wrapper mutable partagé (un tableau ou un SsortingngWrapper hypothétique) contenant la référence (une chaîne ici). Généralement, si vous en avez besoin, vous pouvez trouver un object mutable plus grand que les classes pourront partager.

  public class SsortingngWrapper { public ssortingng s; public SsortingngWrapper(ssortingng s) { this.s = s; } public ssortingng ToSsortingng() { return s; } } public class X { public X() { SsortingngWrapper example = new SsortingngWrapper("X"); new Z(example) System.Diagnostics.Debug.WriteLine( example ); } } public class Z { private SsortingngWrapper _Example; public Z( SsortingngWrapper example ) { this._Example = example; this._Example.s += " (Updated By Z)"; } } 

Comme d’autres l’ont noté, vous ne pouvez pas avoir un champ de type “ref to variable”. Cependant, le simple fait de savoir que vous ne pouvez pas le faire est probablement insatisfaisant; vous voulez probablement aussi savoir d’abord, pourquoi pas, et ensuite, comment contourner cette ressortingction.

La raison en est qu’il n’y a que trois possibilités:

1) Interdit les champs de type ref

2) Autoriser les champs non sécurisés de type ref

3) N’utilisez pas le pool de stockage temporaire pour les variables locales (“la stack”)

Supposons que nous permettions des champs de type ref. Alors vous pourriez faire

 public ref int x; void M() { int y = 123; this.x = ref y; } 

et maintenant on peut y accéder après la fin de M Cela signifie que nous sums dans le cas (2) – l’access à this.x va planter et mourir horriblement parce que le stockage pour y n’existe plus – ou nous sums dans le cas (3), et le y local est stocké le tas ramassé, pas le pool de mémoire temporaire.

Nous aimons l’optimisation selon laquelle les variables locales sont stockées dans le pool temporaire même si elles sont passées par référence, et nous détestons l’idée que vous puissiez laisser une bombe à retardement qui pourrait faire planter votre programme et le faire mourir plus tard. Par conséquent, la première option est: pas de champs de référence.

Notez que pour les variables locales qui sont des variables fermées de fonctions anonymes, nous choisissons l’option (3); ces variables locales ne sont pas allouées à partir du pool temporaire.

Ce qui nous amène ensuite à la deuxième question: comment vous en sortir? Si la raison pour laquelle vous voulez un champ de référence est de créer et de définir une autre variable, c’est parfaitement légal:

 sealed class Ref { private readonly Func getter; private readonly Action setter; public Ref(Func getter, Action setter) { this.getter = getter; this.setter = setter; } public T Value { get { return getter(); } set { setter(value); } } } ... Ref x; void M() { int y = 123; x = new Ref(()=>y, z=>{y=z;}); x.Value = 456; Console.WriteLine(y); // 456 -- setting x.Value changes y. } 

Et voilà. y est stocké sur le tas gc et x est un object qui a la capacité d’obtenir et de définir y .

Notez que le CLR prend en charge les sections locales de référence et les méthodes de renvoi de renvoi, mais pas C #. Peut-être qu’une future version hypothétique de C # prendra en charge ces fonctionnalités; Je l’ai prototypé et ça marche bien. Cependant, ce n’est pas vraiment une priorité sur la liste des priorités, je ne m’attendrais donc pas.

UPDATE: La fonctionnalité mentionnée dans le paragraphe ci-dessus a finalement été implémentée pour de vrai en C # 7. Cependant, vous ne pouvez toujours pas stocker de référence dans un champ.

Vous avez oublié de mettre à jour la référence dans la classe Z:

 public class Z { private ssortingng _Example; public Z(ref ssortingng example) { example = this._Example += " (Updated By Z)"; } } 

Sortie: X (mis à jour par Y) (mis à jour par Z)

Il convient de noter que l’opérateur + = d’une chaîne appelle la méthode Ssortingng.Concat (). Ce qui crée un nouvel object chaîne, il ne met pas à jour la valeur d’une chaîne. Les objects de chaîne sont immuables, la classe de chaîne n’a aucune méthode ou champ permettant de changer la valeur. Très différent du comportement par défaut d’un type de référence régulier.

Donc, si vous utilisez une méthode de chaîne ou un opérateur, vous devez toujours affecter la valeur de retour à une variable. C’est une syntaxe assez naturelle, les types de valeur se comportent de la même manière. Votre code serait très similaire si vous utilisiez un int au lieu d’une chaîne.