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

在C#中修改属性成员时如何触发Setter方法?

问题

我有一个Character类,包含Stat类型的属性。每个角色的Character.Speed属性(类型为Stat)与float类型的ActionValue密切相关,修改速度时需要同步调整ActionValue,机制和《崩坏:星穹铁道》的速度系统一致。现有代码如下:

public class Character {
    private Stat speed;

    public Stat Speed
    {
        get
        { // 这里复制speed是因为我觉得不复制的话会返回引用?如果错了请纠正
            Stat res = new Stat(flatBonus: speed.FlatBonus, percentageBonus:speed.PercentageBonus, baseValue:speed.BaseValue);
            foreach (var statusEffect in speed.StatusEffects)
            {
                res.AddStatusEffect(statusEffect);
            }

            return res;
        }
        set
        {
            float avOld = ActionValue;
            float spdOld = this.speed.GetFinalValue();
            this.speed = value;
            ActionValue = avOld * spdOld / speed.GetFinalValue();
        }
    }
    // ...
}

现在的问题是:Setter只有在Speed的引用改变时才会触发,修改Speed的成员属性(比如FlatBonus)时不会触发Setter。请问有没有我不知道的C#特性,能在修改属性的成员时也触发Setter?也就是不仅在执行character.Speed = ...时触发,执行character.Speed.FlatBonus = ...时也能触发。


解决方案

C#没有原生特性可以直接监听对象内部成员的修改并触发外部属性的Setter。要实现你要的效果,得从Stat类的设计入手,提供可控的修改方式并通知外部:

  • 让Stat类成为不可变类型:每次修改FlatBonus、PercentageBonus等属性时,返回一个新的Stat实例。这样修改速度属性的成员时,必须通过character.Speed = character.Speed.WithFlatBonus(newValue)这类方式,自然会触发Speed的Setter,同步调整ActionValue。示例代码如下:
public class Stat
{
    public float BaseValue { get; }
    public float FlatBonus { get; }
    public float PercentageBonus { get; }
    // 其他成员...

    public Stat(float baseValue, float flatBonus, float percentageBonus)
    {
        BaseValue = baseValue;
        FlatBonus = flatBonus;
        PercentageBonus = percentageBonus;
    }

    public Stat WithFlatBonus(float newFlatBonus)
    {
        return new Stat(BaseValue, newFlatBonus, PercentageBonus);
    }

    // 同理添加WithPercentageBonus、WithBaseValue等方法
}
  • 给Stat类添加事件通知:在Stat的成员属性Setter中触发事件,Character类订阅这个事件,当Stat的内部属性改变时,自动同步ActionValue。示例:
public class Stat
{
    private float _flatBonus;
    public float FlatBonus
    {
        get => _flatBonus;
        set
        {
            if (_flatBonus != value)
            {
                _flatBonus = value;
                OnStatChanged?.Invoke(this, EventArgs.Empty);
            }
        }
    }

    // 其他属性同理添加Setter和事件触发
    public event EventHandler OnStatChanged;
}

public class Character
{
    private Stat _speed;
    private float _lastSpeedValue;
    public float ActionValue { get; set; }

    public Stat Speed
    {
        get => _speed;
        set
        {
            if (_speed != null)
            {
                _speed.OnStatChanged -= HandleStatChanged;
            }
            float avOld = ActionValue;
            float spdOld = _speed?.GetFinalValue() ?? 0;
            _speed = value;
            if (_speed != null)
            {
                _speed.OnStatChanged += HandleStatChanged;
                _lastSpeedValue = _speed.GetFinalValue();
                ActionValue = avOld * spdOld / _lastSpeedValue;
            }
        }
    }

    private void HandleStatChanged(object sender, EventArgs e)
    {
        float spdNew = _speed.GetFinalValue();
        ActionValue = ActionValue * _lastSpeedValue / spdNew;
        _lastSpeedValue = spdNew;
    }
}

另外要注意:你当前的Speed属性Getter返回的是Stat的副本,这会导致外部修改副本的成员时,完全不会影响到Character内部的_speed字段——就算用事件也没用,因为修改的是副本而非内部实例。如果要保留Getter返回副本的逻辑,那只能用不可变Stat的方案,因为外部必须通过替换整个Speed属性来修改值。

内容的提问来源于stack exchange,提问作者Lnio Yarschov

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 09:27:37