【问题标题】:Precompile Exception: Delegate 'Action' does not take 1 arguments预编译异常:委托“操作”不接受 1 个参数
【发布时间】:2020-06-22 12:51:43
【问题描述】:

我正在尝试修复一些从公共存储库中克隆的代码。这是一个缺少await 运算符的async 方法:

public async Task<IEnumerable<JsonPatchOperation>> GetRemoveAllRelationsOperations(IBatchMigrationContext batchContext, WorkItem targetWorkItem)
{
  return targetWorkItem.Relations?.Select((r, index) => MigrationHelpers.GetRelationRemoveOperation(index));
}

我正在尝试这个:

public async Task<IEnumerable<JsonPatchOperation>> GetRemoveAllRelationsOperations(IBatchMigrationContext batchContext, WorkItem targetWorkItem)
{
  return await Task.Run(o => targetWorkItem.Relations?.Select((r, index) => MigrationHelpers.GetRelationRemoveOperation(index)));
}

...但我在 IDE 中遇到错误:

委托 'Action' 不接受 1 个参数

我发现了一些类似的讨论,但不幸的是,它们都没有完全解决 lambda 语法:

似乎预编译器将输入解释为Action,而它应该将其视为Func。但我认为声明o =&gt; ... 可以表明两者之一。

我对 C# 不够熟悉,无法解决这个问题。有人可以帮忙吗?

如何告诉预编译器我要发送Func 而不是Action

【问题讨论】:

    标签: c# async-await action func


    【解决方案1】:

    嗯,编译错误的原因是Task.Run 接受一个(非通用)Action,这是一个不接受任何参数的委托。

    您尝试使用接受参数 o 的 lambda 调用 Task.Run,因此更改为该参数将消除错误:

    Task.Run(() =>
    

    括号 () 表示 lambda 表达式中没有参数。

    话虽如此,在Task.Run 中包装一个同步函数是一种反模式。

    如果您的方法是完全同步的,您最好将其公开:

    public IEnumerable<JsonPatchOperation> GetRemoveAllRelationsOperations(IBatchMigrationContext batchContext, WorkItem targetWorkItem)
    {
        return targetWorkItem.Relations?
            .Select((r, index) => MigrationHelpers.GetRelationRemoveOperation(index));
    }
    

    如果您无法更改签名,例如,如果您正在实现一个接口,那么请改用Task.FromResult

    public async Task<IEnumerable<JsonPatchOperation>> GetRemoveAllRelationsOperations(IBatchMigrationContext batchContext, WorkItem targetWorkItem)
    {
        return Task.FromResult(targetWorkItem.Relations?
            .Select((r, index) => MigrationHelpers.GetRelationRemoveOperation(index)));
    }
    

    这只是将同步结果包装在 Task 对象中,而不是强制 lambda 在线程池上运行。

    【讨论】:

    • 这行得通,谢谢。 wrapping a synchronous function in Task.Run is an anti-pattern 嗯,有趣。 Task.RunTask.Result 有什么区别?
    • @InteXX 不确定你的意思。你能分享你的async/await链吗?
    • "一直异步。"一个async 调用需要一个async 调用,这需要一个async 调用等等。当我们到达调用链的末尾并且最后几行代码是同步的时,如何终止它?从你的回复来看,好像是Task.FromResult,不过我还没有调查过。
    • @InteXX 异步从底部开始,这最终是一个 I/O 操作,然后向上运行,而不是相反。如果你的进程不涉及异步工作,使用Task.Run只会引入线程切换和带来的开销。
    • @InteXX This 是一个很好的系列阅读。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-05
    相关资源
    最近更新 更多