如何在C#中监听ref引用属性的变更?
问题描述
现有如下C#代码,定义了一个返回引用的属性:
public class Foo { public ref Vector2 Bar { get => ref bar; } private Vector2 bar; }
想要实现类似如下set访问器的监听效果,当Bar被修改时触发通知:
set { position = value; NotifyPositionChanged(value); }
理想状态下所有通知逻辑都封装在Foo类内部,但目前想到的方案都无法维持这种封装性,想问这是否真的无法实现,还是有遗漏的思路?
解决方案
直接通过ref返回字段的方式无法直接监听字段的修改——因为外部拿到引用后可以直接操作底层存储,完全绕开类内部的任何拦截逻辑,这正是ref属性的设计目的:让调用方直接操作底层数据。
但要在维持封装性的前提下实现变更通知,有几种可行的替代方案:
1. 改用不可变结构体+显式修改方法
如果Vector2是不可变结构体(或可以修改为不可变),可以去掉ref属性,提供一个专门的修改方法,在方法内部触发通知:
public class Foo { private Vector2 bar; public Vector2 Bar => bar; public void UpdateBar(Vector2 newValue) { bar = newValue; NotifyBarChanged(newValue); } private void NotifyBarChanged(Vector2 value) { // 执行你的通知逻辑 } }
调用方必须通过UpdateBar方法修改值,确保通知逻辑被触发,全程无需暴露内部字段。
2. 封装可变结构体为可观察包装类
如果必须保留直接修改结构体成员的能力,可以把Vector2包装成带有变更通知的类,让Foo内部监听这个包装类的变更事件:
public class ObservableVector2 { private float _x; private float _y; public float X { get => _x; set { _x = value; Changed?.Invoke(this, EventArgs.Empty); } } public float Y { get => _y; set { _y = value; Changed?.Invoke(this, EventArgs.Empty); } } public event EventHandler Changed; // 添加隐式转换,方便和原生Vector2互转 public static implicit operator Vector2(ObservableVector2 vec) => new Vector2(vec.X, vec.Y); public static implicit operator ObservableVector2(Vector2 vec) => new ObservableVector2 { X = vec.X, Y = vec.Y }; } public class Foo { private ObservableVector2 bar = new ObservableVector2(); public ObservableVector2 Bar => bar; public Foo() { // 在内部监听包装类的变更事件 bar.Changed += (_, __) => NotifyBarChanged((Vector2)bar); } private void NotifyBarChanged(Vector2 value) { // 执行你的通知逻辑 } }
调用方修改Bar.X或Bar.Y时,包装类会自动触发变更事件,Foo内部就能捕获并执行通知,全程不需要暴露内部实现细节。
3. 利用.NET的高级拦截特性(复杂度较高)
如果使用.NET Core/.NET 5+,可以借助System.Runtime.CompilerServices的一些特性或动态代理实现拦截,但这种方案复杂度高,一般不推荐用于简单场景。
总结:直接用ref属性返回字段的方式确实无法实现内部监听,必须通过调整设计——要么限制修改方式,要么包装可变值类型来拦截变更,才能在维持封装性的同时实现通知逻辑。
内容的提问来源于stack exchange,提问作者SocratesHat
相关产品推荐
相关产品推荐

