如何为自定义可等待类CommandContext实现ConfigureAwait
为ICommand的可等待CommandContext添加ConfigureAwait支持
现有实现
我实现了一个用于System.Windows.Input.ICommand的可等待基类CommandContext:
public class CommandContext { private SemaphoreSlim _busy { get; } = new SemaphoreSlim(0, 1); public TaskAwaiter GetAwaiter() => _busy .WaitAsync() .GetAwaiter(); public void Release()=>_busy.Release(); }
对应的ICommand定义及实例化:
public ICommand InitDBPIsCommand { get; private set; } // 因为只通过ICommand调用,所以返回void而非Task private async void onInitDBPIs(CommandContext context) { foreach (var dbpi in GetPrivsInUse()) { await initDBPI(dbpi); } context.Release(); } // 实例化命令 InitDBPIsCommand = new Command<CommandContext>(onInitDBPIs);
典型使用方式:
var context = new CommandContext(); Instance.InitDBPIsCommand?.Execute(context); await context;
这套方案运行正常,但我希望能在await context时使用ConfigureAwait(true/false)来控制上下文捕获,却不知道该怎么实现。
解决方案
问题出在原CommandContext直接返回TaskAwaiter,跳过了Task实例,因此无法调用ConfigureAwait方法。我们可以通过修改CommandContext,暴露Task或者直接提供ConfigureAwait方法来解决这个问题。
方案一:直接提供ConfigureAwait方法
修改CommandContext,缓存等待任务并提供ConfigureAwait方法:
public class CommandContext { private readonly SemaphoreSlim _busy = new SemaphoreSlim(0, 1); private Task _waitTask; // 获取等待任务,确保只创建一次 private Task WaitTask => _waitTask ??= _busy.WaitAsync(); // 保留原有的直接await支持 public TaskAwaiter GetAwaiter() => WaitTask.GetAwaiter(); // 提供ConfigureAwait方法,直接返回配置后的可等待对象 public ConfiguredTaskAwaitable ConfigureAwait(bool continueOnCapturedContext) { return WaitTask.ConfigureAwait(continueOnCapturedContext); } public void Release() => _busy.Release(); }
使用方式:
var context = new CommandContext(); Instance.InitDBPIsCommand?.Execute(context); await context.ConfigureAwait(false); // 根据需求传入true或false
方案二:暴露WaitAsync方法返回Task
如果更倾向于保留原生的Task.ConfigureAwait调用方式,可以修改CommandContext暴露WaitAsync方法:
public class CommandContext { private readonly SemaphoreSlim _busy = new SemaphoreSlim(0, 1); private Task _waitTask; private Task WaitTask => _waitTask ??= _busy.WaitAsync(); // 保留直接await的能力 public TaskAwaiter GetAwaiter() => WaitTask.GetAwaiter(); // 暴露WaitAsync方法,让用户自行调用ConfigureAwait public Task WaitAsync() => WaitTask; public void Release() => _busy.Release(); }
使用方式:
var context = new CommandContext(); Instance.InitDBPIsCommand?.Execute(context); await context.WaitAsync().ConfigureAwait(false);
关键说明
- 缓存
_waitTask是为了避免多次调用WaitAsync创建多个独立任务,确保所有等待操作都关联到同一个SemaphoreSlim的等待实例。 - 两种方案都保留了原有的
await context调用方式,同时新增了ConfigureAwait的支持,不需要修改现有命令的执行逻辑。
内容的提问来源于stack exchange,提问作者IV.
相关产品推荐
相关产品推荐

