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

C#属性getter内触发事件后返回值引发栈溢出如何解决

问题根因

你遇到的栈溢出和Invoke方法本身的执行逻辑无关,问题出在UserBalanceNotification的订阅方法中,再次读取了这个带有校验逻辑的Balance属性,形成了「进入getter→触发事件→订阅方读属性→再次进入getter」的无限递归,最终导致栈溢出。

可行解决方案

方案1:调整事件参数,避免订阅方读取属性

你可以修改委托定义,将当前的余额值直接作为事件参数传递给订阅方,订阅方直接使用参数内的余额即可,不需要再读取属性,这是成本最低的解决方案,也符合事件通知的设计逻辑:

// 修改委托定义,增加当前余额参数
public delegate void UserBalanceNotification(User user, int currentBalance, int balanceThreshold);

// getter中调用时传入当前余额
get 
{
    if (_balance <= BalanceThreshold)
       UserBalanceNotification.Invoke(this, _balance, BalanceThreshold);
    return _balance; 
}

方案2:增加递归防重入锁

如果不方便修改事件定义和订阅方逻辑,可以在属性所在类中加一个布尔标记,避免通知触发过程中重复执行校验逻辑:

private bool _isNotificationSending = false;
public int Balance
{
    get 
    {
        // 通知发送过程中直接返回值,跳过校验避免递归
        if (_isNotificationSending) return _balance;
        
        if (_balance <= BalanceThreshold)
        {
            try
            {
                _isNotificationSending = true;
                UserBalanceNotification.Invoke(this, BalanceThreshold);
            }
            finally
            {
                // 无论通知是否执行成功都重置标记
                _isNotificationSending = false;
            }
        }
        return _balance; 
    }
}

方案3:优化逻辑位置(更符合C#规范)

C#的属性设计原则要求getter尽量是无副作用的纯读取操作,你当前把通知逻辑放在getter中,会导致每次读取属性都可能触发事件,不符合常规的使用预期。更合理的做法是把校验逻辑放到Balance属性的setter中,只在余额值被修改到低于阈值的时候触发一次通知,从根源上避免递归问题:

public int Balance
{
    get => _balance;
    set
    {
        _balance = value;
        if (_balance <= BalanceThreshold)
        {
            UserBalanceNotification.Invoke(this, BalanceThreshold);
        }
    }
}

内容的提问来源于stack exchange,提问作者Mads Lildholdt Hansen

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.23 16:06:01