2016-04-06 14 views
2

次のコード...LINQのと非同期ラムダ

using System; 
using System.Linq; 
using System.Threading.Tasks; 

namespace ConsoleAsync 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      MainAsync(args).Wait(); 
      Console.ReadLine(); 
     } 

     static async Task MainAsync(string[] args) 
     { 
      int[] test = new[] { 1, 2, 3, 4, 5 }; 

      if (test.Any(async i => await TestIt(i))) 
       Console.WriteLine("Contains numbers > 3"); 
      else 
       Console.WriteLine("Contains numbers <= 3"); 
     } 

     public static async Task<bool> TestIt(int i) 
     { 
      return await Task.FromResult(i > 3); 
     } 
    } 
} 

はあなたに次のエラーを与える: - あなたは非同期で動作するにはどうすればよいのライン

if (test.Any(async i => await Test.TestIt(i))) 

CS4010: Cannot convert async lambda expression to delegate type 'Func<int, bool>'. An async lambda expression may return void, Task or Task<T>, none of which are convertible to 'Func<int, bool>'.

Lambdasとlinq?

+0

あなたはしないでください。少なくとも真っ直ぐな、すぐに使えるLINQではありません。 Rxを使用してください。 –

+0

@KirillShlenskiyは答えを提出するように感じます... "あなたはできませんが、これは同じことを達成するためにRxを使う方法です"? – Mick

答えて

4

LINQですぐに使用することはできません。しかし、あなたがこの作品を作ることができ、ほとんどの拡張メソッド書き込むことができます。

public static class AsyncExtensions 
{ 
    public static async Task<bool> AnyAsync<T>(
     this IEnumerable<T> source, Func<T, Task<bool>> func) 
    { 
     foreach (var element in source) 
     { 
      if (await func(element)) 
       return true; 
     } 
     return false; 
    } 
} 

をそして、このようにそれを消費:

static async Task MainAsync(string[] args) 
{ 
    int[] test = new[] { 1, 2, 3, 4, 5 }; 

    if (await test.AnyAsync(async i => await TestIt(i)) 
     Console.WriteLine("Contains numbers > 3"); 
    else 
     Console.WriteLine("Contains numbers <= 3"); 
} 

それは私にはちょっと面倒を感じませんが、それはあなたの目標を達成します。

+0

私は自分自身の答えを追加しましたが、本当は答えではないので、あなたの答えにコメントがあります。 – Mick

2

LINQメソッドの小さなサブセットで作業している場合は、@ YuvalItzchakovの答えに従うことをお勧めします。これは、基本クラスライブラリの一部として使用できるコンポーネントのみに依存するためです。

非同期シーケンスに対する豊富なクエリ機能が必要な場合は、代わりにRx.NETを使用できます。 RxはTask -returning代表者と協力それらのいくつかの非同期シーケンスを超えるLINQメソッドの広い配列、すなわちSelectMany提供:

IEnumerable<int> numbers = Enumerable.Range(0, 10); 

IEnumerable<int> evenNumbers = numbers 
    .ToObservable() 
    .SelectMany(async i => new { Value = i, IsMatch = await IsEven(i) }) 
    .Where(a => a.IsMatch) 
    .Select(a => a.Value) 
    .ToEnumerable(); 

async Task<bool> IsEven(int i) 
{ 
    await Task.Delay(100); 

    return i % 2 == 0; 
} 
+0

私はこれを答えとしてマークすることもできます。 – Mick

0

まあ偉大な答えを、私はのYuval Itzchakovのアプローチで成長しているメソッドの数を見ることができました。それは醜いですが、あなたが使用することができます...

public static class AsyncExtensions 
{ 
    public static Func<T, bool> Convert<T>(Func<T, Task<bool>> func) 
    { 
     return t => func(t).Result; 
    } 
} 

そして、このようにそれを消費:醜さのほかに

static async Task MainAsync(string[] args) 
    { 
     int[] test = new[] { 1, 2, 3, 4, 5 }; 

     if (test.Any(i => AsyncExtensions.Convert((Func<int, Task<bool>>)(async x => await TestIt(x)))(i))) 
      Console.WriteLine("Contains numbers > 3"); 
     else 
      Console.WriteLine("Contains numbers < 3"); 
    } 

、当然の大きな問題は、そのブロック、基本的に同期コードを非同期コードを呼び出すことです。私はまだYuval Itzchakovのアプローチを使用しますが、私は非同期のためにLINQをもう一度実装せずにこれを行う方法があることを望みます。

+2

'return t => func(t).Result;' Do not do。それはトラブルを引き起こすつもりです。 –

+0

@YuvalItzchakovはいわかっています。したがって、「醜さの他に、当然の大きな問題は、非同期コードを呼び出す基本的に同期コードである」ということです。要点は、あなたの答えがAnyAsyncの実装を提供している間です....残っているLinq APIの残りの部分は、非同期ラムダでは動作しません。より汎用的な解決策が望ましいだろうが、私が実際には不可能である限り、上記のコードは私が得ることができるほど近くであり、十分ではない。おそらくRxが本当の解決策です – Mick

+0

本当の解決策は、MSFTがBCLに非同期互換LINQ拡張を追加する必要があるということです。彼らはすでに、C#6/7のデザインノートでそれについて話しました。 –

2

How do you work with Async Lambdas and linq?

私はそれを回しても気にしませんか?どのようには彼らをしたいですか?

非同期ストリームの処理を開始するときはいつでも、セマンティクスに関する多くの疑問があります。 LINQのようにWhere節を叩くだけではありません。

この場合、同期ソースシーケンスに適用される「async where」フィルタがあります。非同期コードのアイデアの全体は、非同期操作には時間がかかります(その操作が進行中に呼び出しスレッドを解放したい場合)。フィルタが呼び出されたときにである「どこ非同期」

だから、最初の質問はについて回答します。ソースシーケンスは同期(配列)なので、すべての入力値をすぐに使用できます。 "async where"は、すべての要素の非同期フィルタを同時に開始するか、一度に1つずつ処理する必要がありますか?

これは実際の「非同期場所」ではなく「任意非同期」であった場合、次の問題は、得られる配列(結果が評価され、すなわち、)のを注文あろう。すべての非同期フィルターを同時に開始すると、開始された順序とは異なる順序で完了することができます。非同期フィルタがtrueを返すとすぐに結果の非同期シーケンスが最初の値を生成するか、または結果のシーケンスが元の値を同じ順序で保持するかどうかを指定します(バッファリングを意味します)。

異なるシナリオでは、これらの質問に対して異なる回答が必要です。 Rxはこれらの回答のどれかを表現することができますが、学習するのはむしろ困難です。 Async/awaitは読みやすいが表現力は低い。

これはです(Whereと一般的ではありません)。最初の質問があります。フィルタは同時に実行することも、1つずつ実行することもできますか?

一度に一つあれば、のYuvalのようなアプローチが働くだろう:

bool found = false; 
foreach (var i in test) 
{ 
    if (await TestIt(i)) 
    { 
    found = true; 
    break; 
    } 
} 
if (found) 
    Console.WriteLine("Contains numbers > 3"); 
else 
    Console.WriteLine("Contains numbers <= 3"); 

フィルタはこのような何かそして、同時に実行できる場合は、次の

var tasks = test.Select(i => TestIt(i)).ToList(); 
bool found = false; 
while (tasks.Count != 0) 
{ 
    var completed = await Task.WhenAny(tasks); 
    tasks.Remove(completed); 
    if (await completed) 
    { 
    found = true; 
    break; 
    } 
} 
if (found) 
    Console.WriteLine("Contains numbers > 3"); 
else 
    Console.WriteLine("Contains numbers <= 3"); 
+0

Anyメソッドを使用すると、不要な処理を避けるために、各要素を順番に処理することが望ましいと思います。並行して。 Linqとの注文は、常に別の呼び出しであったため、Whereの結果の順序は任意である可能性があります。 Linqの各メソッドの非同期実装は、メソッドの関数に基づいて異なることになるという良い議論をします。 – Mick

関連する問題