簡體   English   中英

在Linq中異步等待.TakeWhile

[英]Async Await In Linq .TakeWhile

我正在嘗試將我的foreach函數轉換為linq函數

這是我的正常代碼[效果很好]

    var tList = new List<Func<Task<bool>>> { Method1, Method2 };
    tList.Shuffle();

    int succeed = 0;
    foreach (var task in tList)
    {
        var result = await task();

        if(!result)
            break;

        succeed += 1;
    }

    MessageBox.Show(succeed == 1 ? "Loading complete." : "Something went wrong!");

這是轉換為Linq之后的結果[產生2個編譯器錯誤]

    var tList = new List<Func<Task<bool>>> { Method1, Method2 };
    tList.Shuffle();

    int succeed = tList.Select(async task => await task()).TakeWhile(result => result).Count();

    MessageBox.Show(succeed == 1 ? "Loading complete." : "Something went wrong!");

失誤

  • 無法將lambda表達式轉換為委托類型'System.Func,bool>',因為該塊中的某些返回類型不能隱式轉換為
    委托返回類型
  • 無法將類型'System.Threading.Tasks.Task'隱式轉換為'bool'

我不知道為什么編譯器會給出這些消息,所以任何幫助將不勝感激。

注意:我也嘗試了.TakeWhile(async result => await result)與該錯誤

  • 異步方法的返回類型必須為void,Task或Task T

Method1和Method2如果有人想要:

public async Task<bool> Method1()
{
    await Task.Delay(1000);
    Console.WriteLine("Method1");
    return false;
}

public async Task<bool> Method2()
{
    await Task.Delay(1000);
    Console.WriteLine("Method2");
    return true;
}

一種可能的解決方案

使用如下所示的TakeWhileAsync擴展方法:

public static async Task<IEnumerable<T>> TakeWhileAsync<T>(this IEnumerable<Task<T>> tasks, Func<T, bool> predicate)
{
    var results = new List<T>();

    foreach (var task in tasks)
    {
        var result = await task;
        if (!predicate(result))
            break;

        results.Add(result);
    }

    return results;
}

必須有一個更優化的解決方案,但是我認為這很容易理解。 您遍歷任務,等待每個任務完成,然后執行與常規TakeWhile方法相同的邏輯。

首先,您需要實際調用您的方法: tList.Select(func => func()然后在新創建的IEnumerable <Task <bool >>上應用擴展方法: tList.Select(func => func()).TakeWhileAsync(result => result)

然后,您等待它並計算結果:( (await tList.Select(func => func()).TakeWhileAsync(result => result)).Count()

為什么您的嘗試不起作用

.Select(async task => await task()).TakeWhile(result => result)

請記住, 異步函數始終返回Task (或void)。 select塊中的lambda返回IEnumerable <Task <bool >> 在此應用.TakeWhile時,您的TakeWhile謂詞( result => result )也會返回Task <bool> ,因為此時序列中的每個元素都是Task <bool> 這會導致錯誤,因為它應該返回bool

.TakeWhile(async result => await result)

同樣的問題, 您的lambda返回一個Task (因為它是異步的“方法”),它將永遠無法用作LINQ謂詞。

.TakeWhile(result => result.Result)

(根據Simon的回答)該代碼確實可以編譯,甚至可以在某些情況下使用。 (其中SynchronizationContext沒有綁定到單個線程,但這是另一個漫長的話題。)然而,主要的要點是, 阻塞異步代碼很危險,並且有可能導致死鎖 這是一篇非常詳細的文章

第一個代碼和第二個代碼之間有根本的區別(它們不做相同的事情)。

在第一個代碼上,您正在遍歷任務列表。 在第二個步驟中,您只是將每個任務轉換為另一個任務-異步lambda的返回類型將始終是某種任務。

因為您要依次等待每個任務,所以它們實際上是按順序運行的。 那是你打算做的嗎?

嘗試這樣的事情(未經測試):

var tList = new List<Func<Task<bool>>> { Method1, Method2 };
tList.Shuffle();

int succeed = Task.WaitAll(tList.ToArray())
    .TakeWhile(result => result).Count();

MessageBox.Show(
    succeed == 1
    ? "Loading complete."
    : "Something went wrong!");

但這仍然是一段奇怪的代碼。

這僅僅是因為您無法按照編譯器的指示將Task<bool>轉換為bool ..。

更改此:

.TakeWhile(result => result)

..為此:

.TakeWhile(result => result.Result)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM