You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为自定义可等待类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.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 18:40:02