将 C# 异步 Lambda 方法分配给作为任务类型的变量

Eri*_*sen 5 c# vb.net asynchronous task anonymous-function

这在 C# 中可能吗?以下代码产生编译器错误。

HashSet<Task<(string Value, int ToNodeId)>> regionTasks =
  new HashSet<Task<(string Value, int ToNodeId)>>();
foreach (Connection connection in Connections[RegionName])
{
    regionTasks.Add(async () =>
    {        
        string value = await connection.GetValueAsync(Key);
        return (value, connection.ToNode.Id);
    }());
}
Run Code Online (Sandbox Code Playgroud)

C# 编译器抱怨“错误 CS0149:应为方法名称。” 无法推断 lambda 方法的返回类型。

请注意我在 lambda 块关闭后立即通过 () 调用 lambda 方法的技术 {}。这确保Task返回 a ,而不是 a Func

VB.NET 编译器理解这种语法。我惊讶地发现 VB.NET 编译器胜过 C# 编译器的示例。有关完整故事,请参阅我的An Async Lambda Compiler Error Where VB Outsmarts C#博客文章。

Dim regionTasks = New HashSet(Of Task(Of (Value As String, ToNodeId As Integer)))
For Each connection In Connections(RegionName)
    regionTasks.Add(Async Function()
        Dim value = Await connection.GetValueAsync(Key)
        Return (value, connection.ToNode.Id)
    End Function())
Next
Run Code Online (Sandbox Code Playgroud)

VB.NET 编译器了解该End Function()技术。它正确地推断出 lambda 方法的返回类型是Function() As Task(Of (Value As String, ToNodeId As Integer)),因此调用它会返回一个Task(Of (Value As String, ToNodeId As Integer))。这是可分配给regionTasks变量的。

C# 要求我将 lambda 方法的返回值转换为 a Func,这会产生非常难以辨认的代码。

regionTasks.Add(((Func<Task<(string Values, int ToNodeId)>>)(async () =>
{
    string value = await connection.GetValueAsync(Key);
    return (value, connection.ToNode.Id);
}))());
Run Code Online (Sandbox Code Playgroud)

糟糕的。括号太多了!我在 C# 中能做的最好的事情是显式声明 a Func,然后立即调用它。

Func<Task<(string Value, int ToNodeId)>> getValueAndToNodeIdAsync = async () =>
{
    string value = await connection.GetValueAsync(Key);
    return (value, connection.ToNode.Id);
};
regionTasks.Add(getValueAndToNodeIdAsync());
Run Code Online (Sandbox Code Playgroud)

有没有人找到更优雅的解决方案?

Gur*_*ron 3

如果.NET Standard 2.1(或某些 .NET Framework 版本,请参阅兼容性列表)适用于您,您可以通过以下ToHashSet方法使用 LINQ:

var regionTasks = Connections[RegionName]
    .Select(async connection => 
    {        
        string value = await connection.GetValueAsync(Key);
        return (Value: value, ToNodeId: connection.ToNode.Id);
    })
    .ToHashSet();
Run Code Online (Sandbox Code Playgroud)

或者只是HashSet用相应的IEnumerable.

UPD

评论答案中链接的另一个解决方法:

static Func<R> WorkItOut<R>(Func<R> f) { return f; }

foreach (Connection connection in Connections[RegionName])
{
    regionTasks.Add(WorkItOut(async () =>
    {        
        string value = await connection.GetValueAsync(Key);
        return (value, connection.ToNode.Id);
    })());
}
Run Code Online (Sandbox Code Playgroud)