You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 10:10:14