如何通过“引用”分配给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,你的代码将非常相似。