C#:事件多次触发时如何仅执行一次计数逻辑?
批量勾选树形数据点时的性能优化问题
问题场景
项目中通过DatapointInstance类存储树形结构数据,UI复选框绑定RestoreVar属性,勾选父节点时会递归设置所有子节点的RestoreVar值。但每个子节点都会触发RestoreVarChanged事件,导致主类中统计勾选数量的Count方法被频繁调用(百万级数据场景下会执行百万次),性能极差。
现有代码
DatapointInstance类
public class DatapointInstance : INotifyPropertyChanged { private bool restoreVar; private List<DatapointInstance> subVariables; public event PropertyChangedEventHandler PropertyChanged; public event EventHandler RestoreVarChanged; public string Name{ get; set; } public string Value { get; set; } // 其他属性 public bool RestoreVar // 绑定UI复选框 { get { return restoreVar; } set { if (restoreVar != value) { restoreVar = value; OnPropertyChanged(nameof(RestoreVar)); OnRestoreVarChanged(); } } } public List<DatapointInstance> SubVariables // 树形结构子节点 { get { return subVariables; } set { if (subVariables != value) { subVariables = value; OnPropertyChanged(nameof(SubVariables)); } } } protected virtual void OnRestoreVarChanged() { RestoreVarChanged?.Invoke(this, EventArgs.Empty); } protected void OnPropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); foreach (var item in subVariables) { item.RestoreVar = RestoreVar; } } }
主类事件处理代码
private void SomeCheckboxChaneged(object sender, EventArgs e) { UI_Counter = Count(ListofDatapoints); // 统计所有勾选的数据点数量 NotifyPropertyChanged("UI_Counter"); }
解决方案
1. 批量操作时禁用子节点事件触发
在DatapointInstance中添加批量更新标志,批量设置子节点时跳过事件触发,仅在父节点批量操作完成后触发一次事件:
public class DatapointInstance : INotifyPropertyChanged { private bool restoreVar; private List<DatapointInstance> subVariables; private bool isBatchUpdating; // 新增批量更新标志 // ... 原有属性、事件不变 public bool RestoreVar { get { return restoreVar; } set { if (restoreVar != value) { restoreVar = value; OnPropertyChanged(nameof(RestoreVar)); // 仅非批量更新状态下触发事件 if (!isBatchUpdating) { OnRestoreVarChanged(); } } } } protected void OnPropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); if (propertyName == nameof(RestoreVar)) { isBatchUpdating = true; // 开启批量更新模式 foreach (var item in subVariables) { item.RestoreVar = RestoreVar; } isBatchUpdating = false; // 关闭批量更新模式 // 批量操作完成后,父节点主动触发一次事件 OnRestoreVarChanged(); } } }
此方案下,父节点勾选时仅会触发一次RestoreVarChanged事件,主类只需执行一次统计,性能直接优化。
2. 轻量延迟执行(替代定时器)
如果无法修改DatapointInstance类,可在主类中用Task.Delay结合取消令牌合并短时间内的多次事件,仅执行一次统计:
private CancellationTokenSource _delayCts; private async void SomeCheckboxChaneged(object sender, EventArgs e) { // 取消之前未执行的延迟任务 _delayCts?.Cancel(); _delayCts = new CancellationTokenSource(); try { // 延迟100ms(可根据需求调整),等待批量事件结束 await Task.Delay(100, _delayCts.Token); if (!_delayCts.IsCancellationRequested) { UI_Counter = Count(ListofDatapoints); NotifyPropertyChanged("UI_Counter"); } } catch (TaskCanceledException) { // 忽略取消异常,无需处理 } }
该方案无需修改子类,通过合并短时间内的多次事件触发,避免重复执行统计逻辑。
内容的提问来源于stack exchange,提问作者Tom
相关产品推荐
相关产品推荐

