C#の非同期ストリームを使った効率的なデータ処理方法

C#の非同期ストリームは、大量のデータを効率よく処理するための強力なツールです。本記事では、その具体的な使い方と応用例を紹介します。非同期ストリームの基本概念から始まり、具体的な実装例やパフォーマンス最適化のコツ、さらに応用編まで幅広くカバーします。

目次

非同期ストリームの基礎

非同期ストリームは、データの流れを非同期に処理するためのメカニズムです。従来の同期ストリームと異なり、非同期ストリームはデータを逐次処理することなく、待機時間を効率的に利用できます。これにより、大量のデータ処理やI/O操作を行う際にアプリケーションのパフォーマンスが向上します。

非同期ストリームの利点

非同期ストリームを使用する主な利点は以下の通りです:

  • 効率的なリソース利用:スレッドブロッキングを回避し、システムリソースを最適化。
  • スケーラビリティの向上:同時に多くのデータ処理タスクを処理可能。
  • レスポンス時間の短縮:ユーザーインターフェイスが応答しやすくなる。

基本構造

非同期ストリームは、IAsyncEnumerable<T>インターフェイスを用いて定義されます。これにより、非同期でのデータ列挙が可能になります。

public async IAsyncEnumerable<int> GetNumbersAsync()
{
    for (int i = 0; i < 10; i++)
    {
        await Task.Delay(1000); // 非同期の待機
        yield return i;
    }
}

この基本構造を理解することで、非同期ストリームを効率的に活用できます。

基本的な使用方法

非同期ストリームの基本的な使い方を理解するために、以下の例を通して実際に実装を見ていきましょう。

非同期ストリームの定義と使用

非同期ストリームを定義するには、IAsyncEnumerable<T>インターフェイスを使用します。これにより、非同期でデータを生成し、順次処理することが可能になります。

public async IAsyncEnumerable<int> GenerateNumbersAsync()
{
    for (int i = 1; i <= 5; i++)
    {
        await Task.Delay(1000); // 非同期の待機
        yield return i; // 値を逐次返す
    }
}

このメソッドは、1秒ごとに1から5までの数字を生成する非同期ストリームを返します。

非同期ストリームの消費

非同期ストリームを消費するには、await foreachループを使用します。これにより、非同期でデータを受け取ることができます。

public async Task ConsumeNumbersAsync()
{
    await foreach (var number in GenerateNumbersAsync())
    {
        Console.WriteLine(number);
    }
}

この方法で、非同期ストリームからデータを逐次受け取り、コンソールに出力します。

例:非同期ストリームの実装と利用

以下は、非同期ストリームを使って実際に動作する例です。

using System;
using System.Collections.Generic;
using System.Threading.Tasks;

public class AsyncStreamExample
{
    public async IAsyncEnumerable<int> GenerateNumbersAsync()
    {
        for (int i = 1; i <= 5; i++)
        {
            await Task.Delay(1000); // 非同期の待機
            yield return i; // 値を逐次返す
        }
    }

    public async Task ConsumeNumbersAsync()
    {
        await foreach (var number in GenerateNumbersAsync())
        {
            Console.WriteLine(number);
        }
    }

    public static async Task Main(string[] args)
    {
        var example = new AsyncStreamExample();
        await example.ConsumeNumbersAsync();
    }
}

この例を実行すると、1秒ごとに1から5までの数字がコンソールに出力されます。これにより、非同期ストリームの基本的な使用方法を理解できます。

データの読み取り方法

非同期ストリームを使用してデータを読み取る方法について詳述します。非同期ストリームを使うことで、大量のデータを効率よく非同期に読み取ることが可能になります。

非同期ストリームからのデータ読み取り

非同期ストリームからデータを読み取るには、await foreach構文を使用します。この構文を用いることで、非同期にデータを逐次処理することができます。

public async Task ReadDataAsync(IAsyncEnumerable<string> dataStream)
{
    await foreach (var data in dataStream)
    {
        Console.WriteLine(data);
    }
}

このメソッドは、非同期ストリームから文字列データを読み取り、コンソールに出力します。

実践例:ファイルからの非同期データ読み取り

具体的な例として、ファイルから非同期にデータを読み取る方法を紹介します。以下の例では、テキストファイルの各行を非同期ストリームとして読み取ります。

using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;

public class FileReadExample
{
    public async IAsyncEnumerable<string> ReadLinesAsync(string filePath)
    {
        using var reader = new StreamReader(filePath);
        while (!reader.EndOfStream)
        {
            yield return await reader.ReadLineAsync();
        }
    }

    public async Task ProcessFileAsync(string filePath)
    {
        await foreach (var line in ReadLinesAsync(filePath))
        {
            Console.WriteLine(line);
        }
    }

    public static async Task Main(string[] args)
    {
        var example = new FileReadExample();
        await example.ProcessFileAsync("sample.txt");
    }
}

この例では、ReadLinesAsyncメソッドがファイルを非同期に読み取り、各行を非同期ストリームとして返します。そして、ProcessFileAsyncメソッドでそのストリームを消費し、各行をコンソールに出力します。

APIからの非同期データ取得

非同期ストリームを使って、APIからデータを非同期に取得することも可能です。以下の例では、HTTPクライアントを使ってAPIから非同期にデータを取得します。

using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;

public class ApiReadExample
{
    private static readonly HttpClient client = new HttpClient();

    public async IAsyncEnumerable<string> FetchDataFromApiAsync(string apiUrl)
    {
        var response = await client.GetAsync(apiUrl);
        response.EnsureSuccessStatusCode();

        using var stream = await response.Content.ReadAsStreamAsync();
        using var reader = new StreamReader(stream);
        while (!reader.EndOfStream)
        {
            yield return await reader.ReadLineAsync();
        }
    }

    public async Task ProcessApiDataAsync(string apiUrl)
    {
        await foreach (var data in FetchDataFromApiAsync(apiUrl))
        {
            Console.WriteLine(data);
        }
    }

    public static async Task Main(string[] args)
    {
        var example = new ApiReadExample();
        await example.ProcessApiDataAsync("https://api.example.com/data");
    }
}

この例では、FetchDataFromApiAsyncメソッドがAPIからデータを非同期に取得し、非同期ストリームとして返します。そして、ProcessApiDataAsyncメソッドでそのストリームを消費し、取得したデータをコンソールに出力します。

これらの例を通じて、非同期ストリームを使ったデータの読み取り方法を理解することができます。

データの書き込み方法

非同期ストリームを使用してデータを非同期に書き込む方法について解説します。非同期書き込みは、データの保存やログの記録など、さまざまな場面で役立ちます。

非同期ストリームへのデータ書き込み

非同期ストリームへのデータ書き込みは、非同期メソッドと併用して行います。以下の例では、ファイルにデータを非同期に書き込みます。

public async Task WriteDataAsync(IAsyncEnumerable<string> dataStream, string filePath)
{
    using var writer = new StreamWriter(filePath, append: true);
    await foreach (var data in dataStream)
    {
        await writer.WriteLineAsync(data);
    }
}

このメソッドは、非同期ストリームからデータを受け取り、それをファイルに逐次書き込みます。

実践例:非同期にファイルへの書き込み

具体的な例として、非同期にファイルにデータを書き込む方法を紹介します。以下のコードでは、非同期ストリームからデータを受け取り、それをテキストファイルに書き込みます。

using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;

public class FileWriteExample
{
    public async IAsyncEnumerable<string> GenerateDataAsync()
    {
        for (int i = 1; i <= 5; i++)
        {
            await Task.Delay(1000); // 非同期の待機
            yield return $"Data {i}";
        }
    }

    public async Task WriteToFileAsync(string filePath)
    {
        var dataStream = GenerateDataAsync();
        await WriteDataAsync(dataStream, filePath);
    }

    public async Task WriteDataAsync(IAsyncEnumerable<string> dataStream, string filePath)
    {
        using var writer = new StreamWriter(filePath, append: true);
        await foreach (var data in dataStream)
        {
            await writer.WriteLineAsync(data);
        }
    }

    public static async Task Main(string[] args)
    {
        var example = new FileWriteExample();
        await example.WriteToFileAsync("output.txt");
    }
}

この例では、GenerateDataAsyncメソッドが1秒ごとにデータを生成し、非同期ストリームとして返します。そして、WriteToFileAsyncメソッドでそのデータを非同期にファイルに書き込みます。

APIへの非同期データ送信

非同期ストリームを使って、APIにデータを非同期に送信することも可能です。以下の例では、HTTPクライアントを使ってAPIに非同期でデータを送信します。

using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;

public class ApiWriteExample
{
    private static readonly HttpClient client = new HttpClient();

    public async IAsyncEnumerable<string> GenerateApiDataAsync()
    {
        for (int i = 1; i <= 5; i++)
        {
            await Task.Delay(1000); // 非同期の待機
            yield return $"Data {i}";
        }
    }

    public async Task SendDataToApiAsync(string apiUrl)
    {
        var dataStream = GenerateApiDataAsync();
        await foreach (var data in dataStream)
        {
            var content = new StringContent(data, Encoding.UTF8, "application/json");
            await client.PostAsync(apiUrl, content);
        }
    }

    public static async Task Main(string[] args)
    {
        var example = new ApiWriteExample();
        await example.SendDataToApiAsync("https://api.example.com/data");
    }
}

この例では、GenerateApiDataAsyncメソッドが非同期にデータを生成し、SendDataToApiAsyncメソッドがそのデータをAPIに送信します。

これらの例を通じて、非同期ストリームを使ったデータの書き込み方法を理解することができます。

非同期ストリームのエラーハンドリング

非同期ストリームを使用する際には、エラーハンドリングが重要です。非同期処理中に発生する例外を適切に処理することで、アプリケーションの信頼性を向上させることができます。

基本的なエラーハンドリング

非同期ストリーム内で例外が発生した場合、通常の非同期メソッドと同様にtry-catchブロックを使用して例外をキャッチし、適切に処理することができます。

public async IAsyncEnumerable<int> GenerateNumbersWithErrorAsync()
{
    for (int i = 0; i < 10; i++)
    {
        if (i == 5)
        {
            throw new InvalidOperationException("エラーが発生しました!");
        }
        await Task.Delay(500);
        yield return i;
    }
}

消費側でのエラーハンドリング

非同期ストリームの消費側でも例外処理を行う必要があります。await foreach内でtry-catchを使うことで、非同期ストリームからの例外をキャッチし、適切に対処します。

public async Task ConsumeNumbersWithErrorHandlingAsync()
{
    try
    {
        await foreach (var number in GenerateNumbersWithErrorAsync())
        {
            Console.WriteLine(number);
        }
    }
    catch (InvalidOperationException ex)
    {
        Console.WriteLine($"例外が発生しました: {ex.Message}");
    }
}

このメソッドは、GenerateNumbersWithErrorAsyncメソッドからの例外をキャッチし、エラーメッセージをコンソールに出力します。

例外の再スロー

エラーハンドリングの一環として、特定の条件下で例外を再スローすることも可能です。これにより、上位の呼び出し元でさらに高度なエラーハンドリングを実装することができます。

public async Task ConsumeAndRethrowAsync()
{
    try
    {
        await foreach (var number in GenerateNumbersWithErrorAsync())
        {
            Console.WriteLine(number);
        }
    }
    catch (InvalidOperationException ex)
    {
        Console.WriteLine($"例外が発生しました: {ex.Message}");
        throw; // 例外を再スロー
    }
}

実践例:ファイル読み込み時のエラーハンドリング

以下の例では、ファイルを非同期に読み込む際のエラーハンドリングを示します。ファイルが存在しない場合の例外をキャッチして適切に処理します。

public async IAsyncEnumerable<string> ReadLinesWithErrorHandlingAsync(string filePath)
{
    if (!File.Exists(filePath))
    {
        throw new FileNotFoundException("ファイルが見つかりません。", filePath);
    }

    using var reader = new StreamReader(filePath);
    while (!reader.EndOfStream)
    {
        yield return await reader.ReadLineAsync();
    }
}

public async Task ProcessFileWithErrorHandlingAsync(string filePath)
{
    try
    {
        await foreach (var line in ReadLinesWithErrorHandlingAsync(filePath))
        {
            Console.WriteLine(line);
        }
    }
    catch (FileNotFoundException ex)
    {
        Console.WriteLine($"エラー: {ex.Message}");
    }
}

この例では、ファイルが存在しない場合にFileNotFoundExceptionをスローし、ProcessFileWithErrorHandlingAsyncメソッドでその例外をキャッチしてエラーメッセージを表示します。

これらの例を通じて、非同期ストリームのエラーハンドリング方法を理解し、実装することができます。

実践例:ファイルの非同期読み書き

非同期ストリームを用いた実践的な例として、ファイルの非同期読み書きの実装方法を紹介します。このセクションでは、非同期にファイルを読み取り、書き込む方法を詳しく説明します。

非同期にファイルを読み取る方法

まずは、ファイルを非同期に読み取る方法を見ていきます。以下のコードは、テキストファイルの各行を非同期に読み取り、非同期ストリームとして返す方法を示しています。

using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;

public class FileReadWriteExample
{
    public async IAsyncEnumerable<string> ReadLinesAsync(string filePath)
    {
        using var reader = new StreamReader(filePath);
        while (!reader.EndOfStream)
        {
            yield return await reader.ReadLineAsync();
        }
    }

    public async Task DisplayFileContentsAsync(string filePath)
    {
        await foreach (var line in ReadLinesAsync(filePath))
        {
            Console.WriteLine(line);
        }
    }
}

このコードでは、ReadLinesAsyncメソッドが非同期にファイルの各行を読み取り、DisplayFileContentsAsyncメソッドでその内容をコンソールに出力します。

非同期にファイルに書き込む方法

次に、非同期にファイルにデータを書き込む方法を紹介します。以下のコードは、非同期ストリームからデータを受け取り、それをテキストファイルに非同期に書き込む方法を示しています。

public async Task WriteLinesAsync(IAsyncEnumerable<string> dataStream, string filePath)
{
    using var writer = new StreamWriter(filePath, append: true);
    await foreach (var data in dataStream)
    {
        await writer.WriteLineAsync(data);
    }
}

public async Task GenerateAndWriteDataAsync(string filePath)
{
    async IAsyncEnumerable<string> GenerateDataAsync()
    {
        for (int i = 1; i <= 5; i++)
        {
            await Task.Delay(500);
            yield return $"Data {i}";
        }
    }

    var dataStream = GenerateDataAsync();
    await WriteLinesAsync(dataStream, filePath);
}

このコードでは、GenerateAndWriteDataAsyncメソッドが非同期にデータを生成し、それをWriteLinesAsyncメソッドを使ってファイルに書き込みます。

実践例の実行

上記の読み取りと書き込みのメソッドを組み合わせて、ファイルの非同期読み書きを実行します。

public static async Task Main(string[] args)
{
    var example = new FileReadWriteExample();

    string filePath = "example.txt";

    // 非同期にファイルにデータを書き込む
    await example.GenerateAndWriteDataAsync(filePath);

    // 非同期にファイルを読み取って内容を表示する
    await example.DisplayFileContentsAsync(filePath);
}

このコードを実行すると、example.txtファイルに非同期でデータが書き込まれ、その内容がコンソールに表示されます。

これらの例を通じて、非同期ストリームを用いたファイルの非同期読み書き方法を理解することができます。実際のプロジェクトでこれらの技術を活用することで、効率的でスケーラブルなデータ処理が可能になります。

実践例:Webからの非同期データ取得

非同期ストリームを使用して、Webから非同期にデータを取得する方法を紹介します。これにより、ネットワークI/Oを効率的に処理し、大量のデータを効果的に扱うことができます。

非同期にWebデータを取得する方法

まずは、HTTPクライアントを使用して非同期にWebデータを取得する基本的な方法を見ていきます。以下のコードは、HTTPリクエストを送り、レスポンスを非同期に読み取る方法を示しています。

using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;

public class WebDataExample
{
    private static readonly HttpClient client = new HttpClient();

    public async IAsyncEnumerable<string> FetchDataFromApiAsync(string apiUrl)
    {
        var response = await client.GetAsync(apiUrl);
        response.EnsureSuccessStatusCode();

        using var stream = await response.Content.ReadAsStreamAsync();
        using var reader = new StreamReader(stream);
        while (!reader.EndOfStream)
        {
            yield return await reader.ReadLineAsync();
        }
    }

    public async Task DisplayApiDataAsync(string apiUrl)
    {
        await foreach (var line in FetchDataFromApiAsync(apiUrl))
        {
            Console.WriteLine(line);
        }
    }
}

このコードでは、FetchDataFromApiAsyncメソッドが指定されたURLからデータを非同期に取得し、レスポンスの各行を非同期ストリームとして返します。そして、DisplayApiDataAsyncメソッドでそのデータをコンソールに出力します。

実践例:APIからのデータ取得と処理

次に、上記の非同期データ取得メソッドを使って、APIからデータを取得し、そのデータを処理する実例を紹介します。

public static async Task Main(string[] args)
{
    var example = new WebDataExample();
    string apiUrl = "https://api.example.com/data";

    // APIからデータを非同期に取得して表示する
    await example.DisplayApiDataAsync(apiUrl);
}

このコードを実行すると、指定されたAPIからデータが非同期に取得され、その内容がコンソールに出力されます。

APIからのデータ取得と保存

さらに、取得したデータをファイルに非同期で保存する方法を紹介します。以下のコードでは、APIから取得したデータを非同期にファイルに書き込みます。

public async Task SaveApiDataToFileAsync(string apiUrl, string filePath)
{
    var dataStream = FetchDataFromApiAsync(apiUrl);
    await WriteLinesAsync(dataStream, filePath);
}

public async Task WriteLinesAsync(IAsyncEnumerable<string> dataStream, string filePath)
{
    using var writer = new StreamWriter(filePath, append: true);
    await foreach (var data in dataStream)
    {
        await writer.WriteLineAsync(data);
    }
}

public static async Task Main(string[] args)
{
    var example = new WebDataExample();
    string apiUrl = "https://api.example.com/data";
    string filePath = "apiData.txt";

    // APIからデータを非同期に取得してファイルに保存する
    await example.SaveApiDataToFileAsync(apiUrl, filePath);

    // 保存されたデータを非同期に読み取って表示する
    await example.DisplayApiDataAsync(filePath);
}

このコードを実行すると、指定されたAPIからデータが非同期に取得され、それがapiData.txtファイルに保存されます。保存されたデータは、再度非同期に読み取ってコンソールに出力されます。

これらの例を通じて、非同期ストリームを使用したWebからのデータ取得方法を理解し、実際のプロジェクトで活用することができます。

パフォーマンス最適化のコツ

非同期ストリームを使用する際に、プログラムのパフォーマンスを最適化するためのヒントを紹介します。これらのテクニックを活用することで、効率的なデータ処理を実現し、アプリケーションの応答性を向上させることができます。

効率的なバッファリング

データを非同期に処理する際、バッファリングを適切に行うことでパフォーマンスを向上させることができます。バッファリングにより、一度に複数のデータを処理し、I/O操作の回数を減らすことができます。

public async IAsyncEnumerable<int> GenerateNumbersAsync(int count)
{
    var buffer = new List<int>();

    for (int i = 0; i < count; i++)
    {
        buffer.Add(i);

        if (buffer.Count >= 10)
        {
            foreach (var number in buffer)
            {
                yield return number;
            }
            buffer.Clear();
        }
    }

    foreach (var number in buffer)
    {
        yield return number;
    }
}

この例では、10個のデータをバッファリングしてから一度に処理することで、効率的なデータ処理を実現しています。

非同期操作の最小化

非同期操作はリソースを消費するため、必要以上に頻繁に行わないようにすることが重要です。可能な限り、複数の非同期操作をまとめて行うようにしましょう。

public async Task<List<int>> FetchDataAsync()
{
    var results = new List<int>();
    var tasks = new List<Task<int>>();

    for (int i = 0; i < 100; i++)
    {
        tasks.Add(GetDataAsync(i));
    }

    var completedTasks = await Task.WhenAll(tasks);

    results.AddRange(completedTasks);
    return results;
}

private async Task<int> GetDataAsync(int value)
{
    await Task.Delay(100); // 模擬非同期操作
    return value * 2;
}

この例では、複数の非同期操作をまとめて実行することで、パフォーマンスを最適化しています。

非同期ストリームの並行処理

非同期ストリームを並行して処理することで、処理時間を短縮できます。並行処理を適用する際は、データの整合性やリソースの競合に注意が必要です。

public async Task ProcessStreamsInParallelAsync(IEnumerable<IAsyncEnumerable<int>> streams)
{
    var tasks = streams.Select(async stream =>
    {
        await foreach (var item in stream)
        {
            Console.WriteLine(item);
        }
    });

    await Task.WhenAll(tasks);
}

この例では、複数の非同期ストリームを並行して処理し、全体の処理時間を短縮しています。

最適化された非同期APIの使用

非同期ストリームを扱う際は、最適化された非同期APIを使用することが推奨されます。例えば、.NETのSystem.IOSystem.Net.Httpなどの非同期APIは、パフォーマンスが最適化されています。

using System.Net.Http;

public async Task<string> FetchContentAsync(string url)
{
    using var client = new HttpClient();
    return await client.GetStringAsync(url);
}

この例では、HttpClientの非同期メソッドを使用してWebコンテンツを効率的に取得しています。

これらのコツを活用することで、非同期ストリームを使ったプログラムのパフォーマンスを向上させることができます。実際のプロジェクトでこれらの最適化技術を適用し、効率的で応答性の高いアプリケーションを構築しましょう。

応用編:非同期ストリームとLINQ

非同期ストリームとLINQを組み合わせることで、より高度なデータ処理を実現できます。非同期ストリームの強力な機能とLINQの使いやすさを組み合わせることで、データのフィルタリング、変換、および集約を効率的に行うことができます。

非同期ストリームでのLINQの基本使用

非同期ストリームに対してLINQを使用する基本的な方法を紹介します。以下の例では、非同期ストリームからデータを取得し、それをLINQクエリでフィルタリングします。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

public class AsyncStreamWithLinqExample
{
    public async IAsyncEnumerable<int> GenerateNumbersAsync()
    {
        for (int i = 0; i < 20; i++)
        {
            await Task.Delay(100);
            yield return i;
        }
    }

    public async IAsyncEnumerable<int> FilterEvenNumbersAsync()
    {
        await foreach (var number in GenerateNumbersAsync())
        {
            if (number % 2 == 0)
            {
                yield return number;
            }
        }
    }

    public async Task ProcessNumbersAsync()
    {
        var evenNumbers = FilterEvenNumbersAsync();

        await foreach (var number in evenNumbers)
        {
            Console.WriteLine(number);
        }
    }

    public static async Task Main(string[] args)
    {
        var example = new AsyncStreamWithLinqExample();
        await example.ProcessNumbersAsync();
    }
}

この例では、GenerateNumbersAsyncメソッドで生成された非同期ストリームから、FilterEvenNumbersAsyncメソッドで偶数のみをフィルタリングしています。

非同期ストリームのLINQ拡張メソッド

非同期ストリームに対するLINQのような操作を行うために、カスタムの拡張メソッドを作成することができます。以下の例では、Where拡張メソッドを定義しています。

public static class AsyncEnumerableExtensions
{
    public static async IAsyncEnumerable<T> Where<T>(this IAsyncEnumerable<T> source, Func<T, bool> predicate)
    {
        await foreach (var item in source)
        {
            if (predicate(item))
            {
                yield return item;
            }
        }
    }
}

この拡張メソッドを使用して、非同期ストリームに対してフィルタリング操作を行うことができます。

public async Task ProcessNumbersWithCustomLinqAsync()
{
    var numbers = GenerateNumbersAsync();
    var evenNumbers = numbers.Where(n => n % 2 == 0);

    await foreach (var number in evenNumbers)
    {
        Console.WriteLine(number);
    }
}

この例では、カスタム拡張メソッドWhereを使用して、偶数をフィルタリングしています。

非同期ストリームとLINQの組み合わせによる高度な操作

非同期ストリームとLINQを組み合わせて、さらに高度な操作を行うことができます。例えば、データの変換や集約を行うことが可能です。

public async Task TransformAndAggregateAsync()
{
    var numbers = GenerateNumbersAsync();

    var transformedNumbers = numbers
        .Where(n => n % 2 == 0)
        .Select(n => n * 2);

    int sum = 0;
    await foreach (var number in transformedNumbers)
    {
        sum += number;
    }

    Console.WriteLine($"Sum of transformed numbers: {sum}");
}

この例では、偶数をフィルタリングしてから、それらを2倍に変換し、最終的に合計を計算しています。

これらの例を通じて、非同期ストリームとLINQの組み合わせによる高度なデータ処理方法を理解し、実際のプロジェクトで応用することができます。非同期ストリームの利点とLINQの強力なデータ操作機能を組み合わせることで、効率的で読みやすいコードを実現しましょう。

演習問題

これまでの内容を理解し、実践するための演習問題をいくつか用意しました。これらの演習を通じて、非同期ストリームとLINQを使ったデータ処理の技術をさらに深めることができます。

演習1: 非同期ストリームで偶数をフィルタリング

以下の要件を満たす非同期ストリームを作成してください。

  1. 1から20までの整数を生成する非同期ストリームを作成します。
  2. 生成した整数の中から偶数のみをフィルタリングします。
  3. フィルタリングした偶数をコンソールに出力します。
public class AsyncStreamExercises
{
    public async IAsyncEnumerable<int> GenerateNumbersAsync(int start, int count)
    {
        for (int i = start; i < start + count; i++)
        {
            await Task.Delay(100);
            yield return i;
        }
    }

    public async IAsyncEnumerable<int> FilterEvenNumbersAsync(IAsyncEnumerable<int> numbers)
    {
        await foreach (var number in numbers)
        {
            if (number % 2 == 0)
            {
                yield return number;
            }
        }
    }

    public async Task ExecuteExercise1()
    {
        var numbers = GenerateNumbersAsync(1, 20);
        var evenNumbers = FilterEvenNumbersAsync(numbers);

        await foreach (var number in evenNumbers)
        {
            Console.WriteLine(number);
        }
    }
}

演習2: 非同期にファイル読み書き

以下の要件を満たす非同期ストリームを作成してください。

  1. テキストファイルから非同期に各行を読み取る非同期ストリームを作成します。
  2. 読み取った内容を別のテキストファイルに非同期で書き込みます。
public async Task Exercise2_ReadAndWriteFileAsync(string inputFilePath, string outputFilePath)
{
    async IAsyncEnumerable<string> ReadLinesAsync(string filePath)
    {
        using var reader = new StreamReader(filePath);
        while (!reader.EndOfStream)
        {
            yield return await reader.ReadLineAsync();
        }
    }

    async Task WriteLinesAsync(IAsyncEnumerable<string> lines, string filePath)
    {
        using var writer = new StreamWriter(filePath, append: true);
        await foreach (var line in lines)
        {
            await writer.WriteLineAsync(line);
        }
    }

    var lines = ReadLinesAsync(inputFilePath);
    await WriteLinesAsync(lines, outputFilePath);
}

演習3: Webデータのフィルタリングと保存

以下の要件を満たす非同期ストリームを作成してください。

  1. 指定されたURLからデータを非同期に取得する非同期ストリームを作成します。
  2. 取得したデータから特定のキーワードを含む行のみをフィルタリングします。
  3. フィルタリングしたデータをテキストファイルに非同期で書き込みます。
public async Task Exercise3_FetchFilterAndSaveAsync(string apiUrl, string keyword, string outputFilePath)
{
    async IAsyncEnumerable<string> FetchDataFromApiAsync(string url)
    {
        var response = await client.GetAsync(url);
        response.EnsureSuccessStatusCode();

        using var stream = await response.Content.ReadAsStreamAsync();
        using var reader = new StreamReader(stream);
        while (!reader.EndOfStream)
        {
            yield return await reader.ReadLineAsync();
        }
    }

    async IAsyncEnumerable<string> FilterLinesAsync(IAsyncEnumerable<string> lines, string keyword)
    {
        await foreach (var line in lines)
        {
            if (line.Contains(keyword))
            {
                yield return line;
            }
        }
    }

    async Task WriteLinesAsync(IAsyncEnumerable<string> lines, string filePath)
    {
        using var writer = new StreamWriter(filePath, append: true);
        await foreach (var line in lines)
        {
            await writer.WriteLineAsync(line);
        }
    }

    var fetchedData = FetchDataFromApiAsync(apiUrl);
    var filteredData = FilterLinesAsync(fetchedData, keyword);
    await WriteLinesAsync(filteredData, outputFilePath);
}

これらの演習問題に取り組むことで、非同期ストリームとLINQを活用したデータ処理のスキルをさらに磨くことができます。各演習を通じて、非同期処理の理解を深め、実際のプロジェクトに応用できる知識を身につけましょう。

まとめ

本記事では、C#の非同期ストリームを使った効率的なデータ処理方法について詳しく解説しました。非同期ストリームの基礎から始まり、データの読み取り・書き込み方法、エラーハンドリング、実践例としてファイルやWebデータの非同期処理、そしてLINQとの組み合わせによる高度なデータ操作まで幅広くカバーしました。

非同期ストリームを活用することで、大量のデータを効率的に処理し、アプリケーションのパフォーマンスと応答性を向上させることができます。今回の内容を参考に、実際のプロジェクトで非同期ストリームを活用し、効果的なデータ処理を実現してください。

コメント

コメントする

目次