C#异步场景下属性与方法选型及强制重载方案咨询
适配延迟加载+强制重载场景的实现方案
问题核心
需要实现类中多个属性的异步延迟加载,同时支持强制重载(刷新),还要避免同步阻塞和方法膨胀的问题。
优化方案:封装可刷新的异步延迟加载容器
自己封装通用的AsyncRefreshableLazy<T>类,把延迟加载、异步处理、强制刷新的逻辑抽离,避免每个属性重复写判断代码,也不用给每个属性加单独的GetXXXAsync方法。
通用容器实现
public class AsyncRefreshableLazy<T> where T : class { private readonly Func<Task<T>> _factory; private Task<T>? _cachedTask; public AsyncRefreshableLazy(Func<Task<T>> factory) { _factory = factory ?? throw new ArgumentNullException(nameof(factory)); } public async Task<T> GetValueAsync(bool forceRefresh = false) { if (forceRefresh || _cachedTask == null) { _cachedTask = _factory(); } return await _cachedTask.ConfigureAwait(false); } // 可选:同步获取(仅兼容旧代码,不推荐) public T GetValue(bool forceRefresh = false) { return GetValueAsync(forceRefresh).GetAwaiter().GetResult(); } }
在业务类中的使用
public class YourBusinessClass { private readonly AsyncRefreshableLazy<EntArchElement> _classifier; private readonly AsyncRefreshableLazy<AnotherSubType> _anotherProperty; // 其他子类型属性... public YourBusinessClass(IRepository repository, Guid classifierGuid, Guid anotherGuid) { _classifier = new AsyncRefreshableLazy<EntArchElement>( () => repository.GetElementAsync(classifierGuid)); _anotherProperty = new AsyncRefreshableLazy<AnotherSubType>( () => repository.GetAnotherElementAsync(anotherGuid)); // 初始化其他属性... } // 对外暴露异步属性(C# 8.0+支持) public Task<EntArchElement> Classifier => _classifier.GetValueAsync(); // 带强制重载参数的方法,统一调用风格 public Task<EntArchElement> GetClassifierAsync(bool forceRefresh = false) { return _classifier.GetValueAsync(forceRefresh); } // 其他属性同理 public Task<AnotherSubType> AnotherProperty => _anotherProperty.GetValueAsync(); public Task<AnotherSubType> GetAnotherPropertyAsync(bool forceRefresh = false) { return _anotherProperty.GetValueAsync(forceRefresh); } }
方案优势
- 代码复用:所有延迟加载+刷新逻辑都在通用容器中,业务类无需重复编写判断代码
- 异步友好:全程异步操作,避免
.Result阻塞线程 - 灵活调用:既可以用异步属性简化常规获取,也可以用带
forceRefresh参数的方法触发重载 - 线程安全扩展:如果需要多线程场景,可给
_cachedTask的赋值加锁或用Interlocked.Exchange保证原子性,示例如下:
线程安全版容器扩展
public async Task<T> GetValueAsync(bool forceRefresh = false) { if (!forceRefresh && _cachedTask != null) { return await _cachedTask.ConfigureAwait(false); } var newTask = _factory(); var oldTask = Interlocked.Exchange(ref _cachedTask, newTask); // 非强制刷新时,优先用旧任务避免并发重复加载 if (!forceRefresh && oldTask != null) { return await oldTask.ConfigureAwait(false); } return await newTask.ConfigureAwait(false); }
替代思路:属性包装器(C# 9.0+)
如果使用C# 9.0及以上版本,可尝试用属性包装器进一步简化,但目前C#原生不支持自定义属性包装器,需借助Source Generator或第三方库实现,相比之下通用容器方案更通用且无额外依赖。
内容的提问来源于stack exchange,提问作者Aleš
相关产品推荐
相关产品推荐

