.NET是否内置支持带计数的ViewModel IsBusy状态处理?
问题描述
我的视图模型(ViewModel)使用IsBusy属性指示后台处理是否进行,以此驱动UI指示器。但忙碌状态并非互斥,有时多个后台操作可同时进行,因此我用计数而非布尔值来维护IsBusy状态,定义了如下IBusy接口:
public interface IBusy { void IncrementBusyCount(); bool DecrementBusyCount(); bool IsBusy { get; } }我在视图模型上实现该接口,使IsBusy在首个后台操作启动时变为true,最后一个操作结束时变为false:
public class MyBusyVm : ViewModelBase, IBusy { /* ... */}我还编写了实现IDisposable的BusyLock包装类,自动调用IncrementBusyCount和DecrementBusyCount,无需每次使用try/finally:
void DoWorkOnBackgroundThread(MyBusyVm vm) { using myLock = new BusyLock(vm); // ...执行耗时后台工作 // ...计数在此自动递减 }这是很常见的设计模式,我认为.NET中应该已有更完善的内置实现,是否存在?(多年前我曾自行实现Lazy和ISupportInitialize,不想重蹈覆辙)(补充:我知道可以用Semaphore,但希望避免内核对象)
解答
.NET框架及.NET Core/.NET 5+中没有完全匹配该需求的内置开箱实现,但你可以借助框架提供的基础类型简化实现,无需从头构建核心逻辑:
- 原子计数用
Interlocked类替代手动实现:多线程场景下的计数增减需要保证原子性,Interlocked.Increment和Interlocked.Decrement是用户态操作,不会用到内核对象,完美契合你的性能要求,同时能确保线程安全。 BusyLock这类IDisposable包装器虽无内置,但可基于Interlocked快速实现:结合Interlocked的原子操作,你可以写出轻量且可靠的版本,示例如下:
// 基于Interlocked实现的IBusy(适配MVVM的通知机制) public class BusyManager : IBusy, INotifyPropertyChanged { private int _busyCount; public bool IsBusy => _busyCount > 0; public void IncrementBusyCount() { Interlocked.Increment(ref _busyCount); OnPropertyChanged(nameof(IsBusy)); } public bool DecrementBusyCount() { var newCount = Interlocked.Decrement(ref _busyCount); OnPropertyChanged(nameof(IsBusy)); return newCount >= 0; } public event PropertyChangedEventHandler? PropertyChanged; protected virtual void OnPropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } // 轻量BusyLock实现 public readonly struct BusyLock : IDisposable { private readonly IBusy _busyManager; public BusyLock(IBusy busyManager) { _busyManager = busyManager; _busyManager.IncrementBusyCount(); } public void Dispose() { _busyManager?.DecrementBusyCount(); } }
这种实现既满足了多线程下的计数需求,又避免了内核对象的开销,同时复用了.NET内置的原子操作逻辑,不用重复造轮子。
内容的提问来源于stack exchange,提问作者Joe
相关产品推荐
相关产品推荐

