如何通过“引用”分配给C#中的类字段?

我想了解如何通过“引用”分配给C#中的类字段。

我有以下例子来考虑:

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(); 

运行上面的代码时,输​​出是:

X(由Y更新)

并不是:

X(由Y更新)(由Z更新)

正如我所希望的那样。

似乎将一个“ref参数”分配给一个字段失去了参考。

分配给字段时有什么办法保持参考?

谢谢。

No. ref纯粹是一个调用约定。 你不能用它来限定一个字段。 在Z中,_Example被设置为传入的string引用的值。然后使用+ =为其分配一个新的string引用。 你从来没有分配的例子,所以裁判没有效果。

唯一的解决办法是有一个共享的可变包装对象(一个数组或一个假想的StringWrapper),它包含引用(这里是一个string)。 一般来说,如果你需要这个,你可以find一个更大的可变对象来分享。

  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)"; } } 

正如其他人所指出的,你不能有一个“引用variables”types的字段。 但是,只要知道你不能做到这一点,可能是不满意的。 你可能也想先了解一下,为什么不呢,其次是如何绕过这个限制。

原因是因为只有三种可能性:

1)不允许reftypes的字段

2)允许不安全的reftypes的字段

3)不要使用临时存储池作为局部variables(又称“堆栈”)

假设我们允许reftypes的字段。 那你可以做

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

现在y可以在M完成后被访问。 这意味着,要么我们在情况(2) – 访问this.x将崩溃并死亡可怕,因为y的存储不再存在 – 或者我们在情况(3),并且本地y存储在垃圾收集堆,而不是临时内存池。

我们喜欢局部variables存储在临时池中的优化,即使它们被ref传递,我们也讨厌你可以留下一个定时炸弹,这可能会让你的程序崩溃,以后死掉。 因此,选项一是:没有引用字段。

请注意,对于匿名函数的闭合variables的局部variables,我们select选项(3); 这些局部variables不会被分配出临时池。

然后,我们来看第二个问题:你如何解决这个问题? 如果你想要一个引用字段的原因是让另一个variables的getter和setter,这是完全合法的:

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

你去了。 y存储在gc堆中, x是一个能够获取和设置y

请注意,CLR确实支持ref locals和ref返回方法,虽然C#没有。 也许假设未来版本的C#将支持这些function; 我已经原型,它运作良好。 但是,这并不是重点列表中的真正高,所以我不会抱有希望。

你忘了更新Z类的参考资料:

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

输出:X(由Y更新)(由Z更新)

要记住的一点是,string的+ =运算符调用String.Concat()方法。 它创build一个新的string对象,它不会更新string的值。 string对象是不可变的,string类没有任何方法或字段可以让您更改值。 与常规引用types的默认行为非常不同。

所以,如果使用string方法或运算符,则必须将返回值分配回variables。 这是非常自然的语法,值types的行为是相同的。 如果你使用int而不是string,你的代码将非常相似。