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
相关产品推荐
相关产品推荐

