C#7 ref返回特性:内存留存的是类实例还是返回的引用属性?
C#7 ref返回特性:内存留存的是类实例还是仅引用的属性?
咱们直接把问题拆开来聊,先搞懂ref返回的本质,再解答你的疑问:
第一个问题:调用GetData()后内存里留存什么?
当你通过ref返回一个类(引用类型)实例的字段时,内存里会留存完整的MyDataStructure实例,而不是仅仅那个整数。
原因很直白:ref返回的是该int字段在内存中的直接地址,而这个地址属于整个MyDataStructure实例内存块的一部分。GC的回收逻辑是看有没有「根引用」关联到实例的任何部分——只要你还持有这个ref,就相当于给整个实例挂了根引用,GC就不会回收它,所以整个实例都会留在内存里。
第二个问题:为什么示例中的s无法被留存?
我猜你大概率写了类似这样的代码:
public ref int GetData() { MyDataStructure s = new MyDataStructure(); // 局部变量s return ref s.Value; }
这种情况编译器直接会报错,根本不让你通过,更别说留存s了。
核心原因是:s是方法内部的局部引用变量,它的作用域只限于这个方法。方法执行完毕后,栈上的s变量会被销毁,堆上的MyDataStructure实例此时没有任何合法的根引用——虽然你返回的ref指向它的字段,但这种指向局部实例字段的ref属于「悬空引用风险」,C#编译器为了避免你拿到指向已回收内存的无效引用,直接禁止了这种写法。
如果想让s的实例被留存,得让它的生命周期超出方法范围,比如把s设为类的成员变量,或者用ref/in修饰传入方法的参数(保证它的生命周期足够),举个合法的例子:
private MyDataStructure _s = new MyDataStructure(); // 类成员变量 public ref int GetData() { return ref _s.Value; }
这种情况下,只要你的类实例还活着,_s的实例就会被留存,返回的ref也能安全指向它的字段。
内容的提问来源于stack exchange,提问作者Coder14
相关产品推荐
相关产品推荐

