Como faço para atribuir por “referência” para um campo de class em c #?

Eu estou tentando entender como atribuir por “referência” a um campo de class em c #.

Eu tenho o seguinte exemplo a considerar:

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

Ao executar o código acima, a saída é:

X (atualizado por Y)

E não:

X (atualizado por Y) (atualizado por Z)

Como eu esperava.

Parece que atribuir um “parâmetro ref” a um campo perde a referência.

Existe alguma maneira de manter a referência ao atribuir a um campo?

Obrigado.

Não. Ref é puramente uma convenção de chamada. Você não pode usá-lo para qualificar um campo. Em Z, _Exemplo é definido como o valor da referência de cadeia passada. Você, então, atribui uma nova referência de cadeia a ele usando + =. Você nunca atribui ao exemplo, então o juiz não tem efeito.

A única solução alternativa para o que você deseja é ter um object de wrapper mutável compartilhado (uma matriz ou um StringWrapper hipotético) que contenha a referência (uma string aqui). Geralmente, se você precisar disso, poderá encontrar um object mutável maior para as classs compartilharem.

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

Como outros notaram, você não pode ter um campo do tipo “ref to variable”. Entretanto, apenas saber que você não pode fazer isso é provavelmente insatisfatório; você provavelmente também quer saber primeiro, porque não, e segundo, como contornar essa restrição.

A razão é porque existem apenas três possibilidades:

1) Não permitir campos do tipo ref

2) Permitir campos inseguros do tipo ref

3) Não use o conjunto de armazenamento temporário para variables ​​locais (também conhecidas como “a pilha”)

Suponha que permitimos campos do tipo ref. Então você poderia fazer

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

e agora y pode ser acessado após M concluir. Isto significa que ou estamos no caso (2) – acessando this.x irá falhar e morrer horrivelmente porque o armazenamento para y não existe mais – ou estaremos no caso (3), e o y local é armazenado em o heap do lixo coletado, não o pool de memory temporário.

Nós gostamos da otimização que as variables ​​locais são armazenadas no pool temporário, mesmo se elas estão sendo passadas por ref, e nós odiamos a idéia de que você poderia deixar uma bomba de tempo ao redor que poderia fazer seu programa falhar e morrer mais tarde. Portanto, a primeira opção é: sem campos ref.

Note que, para variables ​​locais que são variables ​​de funções anônimas, escolhemos a opção (3); essas variables ​​locais não são alocadas fora do pool temporário.

O que então nos leva à segunda pergunta: como você contorna isso? Se o motivo pelo qual você quer um campo de referência é fazer um getter e setter de outra variável, isso é perfeitamente legal:

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

E ai você vai. y é armazenado no heap gc e x é um object que possui a capacidade de obter e definir y .

Observe que o CLR oferece suporte a ref locals e ref retornando methods, embora C # não. Talvez uma versão futura hipotética do C # ofereça suporte a esses resources; Eu fiz um protótipo e funciona bem. No entanto, isso não é real alto na lista de prioridades, então eu não teria minhas esperanças.

ATUALIZAÇÃO: O recurso mencionado no parágrafo acima foi finalmente implementado para real no C # 7. No entanto, você ainda não pode armazenar um ref em um campo.

Você esqueceu de atualizar a referência na class Z:

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

Saída: X (atualizado por Y) (atualizado por Z)

Um ponto a ter em mente é que o operador + = para uma string chama o método String.Concat (). Que cria um novo object de string, ele não atualiza o valor de uma string. Objetos String são imutáveis, a class string não possui nenhum método ou campo que permita alterar o valor. Muito diferente do comportamento padrão de um tipo de referência regular.

Portanto, se você usar um método ou operador de string, sempre terá que atribuir o valor de retorno a uma variável. Essa é uma syntax bastante natural, os tipos de valor se comportam da mesma maneira. Seu código seria muito semelhante se você usasse um int em vez de uma string.