rksoftware

Visual Studio とか C# とかが好きです

C# 9.0 の確認「foreach ループの拡張機能 GetEnumerator サポート」

C# 9.0 の確認の目次はこちら

■ foreach ループの拡張機能 GetEnumerator サポート

ドキュメントはこちら

適合性と完成度の機能 という項目の結構読み進めたところにある 「また、foreach ループによって」 から始まるブロックです。
簡単に言うと拡張メソッドで GetEnumerator メソッドを作ればどんな型のオブジェクトでも foreach できます。

■ 検証コード

例えばこんなデータクラスがあったとします。

class Data
{
    public string MyProperty1 { get; set; }
    public string MyProperty2 { get; set; }
    public string MyProperty3 { get; set; }
}

1、2、3 の 3 つのプロパティを持っています。こういったクラスの場合、プロパティを一つずつ上から順に処理をしたい、そう思うことが割とよくあると思います。
そして順に処理するといえば我らが foreach です。しかし、次のようなコードは当然ながらエラーになってしまいます。

class Program
{
    static void Main(string[] args)
    {
        var data = new Data
        {
            MyProperty1 = "Saitama"
            , MyProperty2 = "Chiba"
            , MyProperty3 = "Ibaraki"
        };

        foreach (string datum in data)
            Console.WriteLine(datum);

        Console.WriteLine("Hello World!");
    }
}

class Data
{
    public string MyProperty1 { get; set; }
    public string MyProperty2 { get; set; }
    public string MyProperty3 { get; set; }
}

■ エラーメッセージ

エラー    CS1579  'Data' は 'GetEnumerator' のパブリック インスタンスまたは拡張機能の定義を含んでいないため、型 'Data' の変数に対して foreach ステートメントを使用することはできません

当然の結果ですね。

■ 拡張メソッド GetEnumerator

そこで C# 9 環境で拡張メソッドを作って試してみます。

※検証用コードです。ベストプラクティスをご提案するものではありません。それっぽいコード書いてみたら無駄に長くて焦点がぼやけたので、手抜き実装です。

static class DataExtensions
{
    internal static System.Collections.IEnumerator GetEnumerator(this Data data)
        =>
        new string[]
        {
            data?.MyProperty1
            , data?.MyProperty2
            , data?.MyProperty3
        }
        .GetEnumerator();
}

■ 検証コード

コード全体はこんな感じになりました。

class Program
{
    static void Main(string[] args)
    {
        var data = new Data
        {
            MyProperty1 = "Saitama"
            , MyProperty2 = "Chiba"
            , MyProperty3 = "Ibaraki"
        };

        foreach (string datum in data)
            Console.WriteLine(datum);

        Console.WriteLine("Hello World!");
    }
}

class Data
{
    public string MyProperty1 { get; set; }
    public string MyProperty2 { get; set; }
    public string MyProperty3 { get; set; }
}

static class DataExtensions
{
    internal static System.Collections.IEnumerator GetEnumerator(this Data data)
        =>
        new string[]
        {
            data?.MyProperty1
            , data?.MyProperty2
            , data?.MyProperty3
        }
        .GetEnumerator();
}

■ 実行結果

Saitama
Chiba
Ibaraki
Hello World!

計画通り! プロパティの値が順に処理されました。

■ まとめ

この機能を知らないままに、この機能を使って書かれたコードに出会うと積みそうです。
間違いなくしっかり覚えておきましょう。便利な機能なので流行ると思います。備えましょう。