![](/img/trans.png)
[英]Cannot assign method group to an implicitly-typed local variable in asp.net,linq,c#
[英]Assign C# Async Lambda Method to Variable Typed as a Task
這在 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);
}());
}
C# 編譯器報錯,“錯誤 CS0149:需要方法名稱。” 它無法推斷 lambda 方法的返回類型。
請注意我在 lambda 塊關閉 {} 后立即通過 () 調用 lambda 方法的技術。 這可確保返回Task
而不是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
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 方法的返回值轉換為Func
,這會產生非常難以辨認的代碼。
regionTasks.Add(((Func<Task<(string Values, int ToNodeId)>>)(async () =>
{
string value = await connection.GetValueAsync(Key);
return (value, connection.ToNode.Id);
}))());
糟糕的。 括號太多了! 我在 C# 中能做的最好的事情就是明確聲明一個Func
,然后立即調用它。
Func<Task<(string Value, int ToNodeId)>> getValueAndToNodeIdAsync = async () =>
{
string value = await connection.GetValueAsync(Key);
return (value, connection.ToNode.Id);
};
regionTasks.Add(getValueAndToNodeIdAsync());
有沒有人找到更優雅的解決方案?
如果您可以使用.NET Standard 2.1
(或某些 .NET 框架版本,請參閱兼容性列表),您可以將 LINQ 與ToHashSet
方法一起使用:
var regionTasks = Connections[RegionName]
.Select(async connection =>
{
string value = await connection.GetValueAsync(Key);
return (Value: value, ToNodeId: connection.ToNode.Id);
})
.ToHashSet();
或者只是用相應的IEnumerable
初始化HashSet
。
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);
})());
}
當我第一次閱讀您的問題的標題時,我想“嗯?誰會建議嘗試將 x 類型的值分配給 y 類型的變量,而不是與 x 的 inheritance 關系?這就像嘗試將 int 分配給字符串。 ..”
我閱讀了代碼,然后更改為“好的,這不是為任務分配委托,這只是創建一個任務並將其存儲在任務集合中。但看起來他們確實在分配一個委托到一個任務...
然后我看到了
請注意我在 lambda 塊關閉 {} 后立即通過 () 調用 lambda 方法的技術。 這確保了返回一個任務,而不是一個函數。
你必須用評論來解釋這一點,這意味着這是一種代碼味道,而且是錯誤的做法。 您的代碼已經從可讀的自我記錄變成了代碼高爾夫練習,使用了聲明委托並立即執行它以創建任務的神秘語法技巧。 這就是我們擁有Task.Run
/ TaskFactory.StartNew
的目的,這也是我所見過的所有 TAP 代碼在需要任務時所做的事情
您會注意到此表單有效並且不會產生錯誤:
HashSet<Task<(string Value, int ToNodeId)>> regionTasks =
new HashSet<Task<(string Value, int ToNodeId)>>();
foreach (Connection connection in Connections[RegionName])
{
regionTasks.Add(Task.Run(async () =>
{
string value = await connection.GetValueAsync(Key);
return (value, connection.ToNode.Id);
}));
}
它的工作原理要清楚得多,而且您在不鍵入Task.Run
時保存的 7 個字符意味着您不必編寫 50 多個字符的注釋來解釋為什么可以將看起來像委托的東西分配給 Task 類型的變量
我會說 C# 編譯器可以讓你避免在這里編寫糟糕的代碼,這是 VB 編譯器讓開發人員快速松散並編寫難以理解的代碼的另一種情況
調用異步 lambda 以獲取具體化任務的一種簡單方法是使用幫助程序 function,如下面的Materialize
:
public static Task Materialize(Func<Task> taskFactory) => taskFactory();
public static Task<T> Materialize<T>(Func<Task<T>> taskFactory) => taskFactory();
使用示例:
regionTasks.Add(Materialize(async () =>
{
string value = await connection.GetValueAsync(Key);
return (value, connection.ToNode.Id);
}));
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.