1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

GetEnumerator拡張メソッドを使って複数のIEnumerableオブジェクトをまとめてforeachで処理する

Posted at

はじめに

本記事はGetEnumerator拡張メソッドを使って複数のIEnumerableオブジェクトをまとめてforeachで処理する方法をまとめることを目的とします。

以前投稿した記事での実現方法

以前投稿したこちらの記事では複数のIEnumerableオブジェクトをまとめてforeachで処理するのにLinqのZipメソッドを利用していました。
具体的にはページ名一覧とユーザーID一覧のIEnumerableオブジェクトを一つのIEnumerableオブジェクトにまとめて、foreachで処理しています。以下のような感じです。

// ページ名一覧
IEnumerable<string> pageNames = ...;
// ユーザーID一覧
IEnumerable<string> userIds = ...;

// ページごとのユニークIDを取得するためにページ名一覧とユーザーID一覧を1つのEnumerableにまとめる
var pageNameAndUserIds = pageNames.Zip(userIds, (pageName, userId) => new { pageName, userId });

// ページ名とユーザーIDの組み合わせ一覧に対して反復処理をする
foreach (var pageNameAndUserId in pageNameAndUserIds)
{
    // ページ名
    var targetPageName = pageNameAndUserId.pageName;
    // ユーザーID
    var targetUserId = pageNameAndUserId.userId;
    ....
}

GetEnumerator拡張メソッドを使う場合

C#9.0の新機能であるGetEnumerator拡張メソッドを使うことで、任意の型に対してイテレート(foreach)処理をするための列挙子を生成する処理の定義が可能になります。
前章のサンプルをGetEnumerator拡張メソッドを使うようにすると以下のようになります。

// ページ名一覧
IEnumerable<string> pageNames = ...;
// ユーザーID一覧
IEnumerable<string> userIds = ...;

// ページ名とユーザーIDの組み合わせ一覧に対して反復処理をする
foreach (var (pageName, userId) in (pageNames, userIds))
{
    // ページ名
    var targetPageName = pageName;
    // ユーザーID
    var targetUserId = userId;
    ....
}

/// <summary>
/// 拡張メソッドを定義するクラス
/// </summary>
public static class Extensions
{
	/// <summary>
	/// string型のIEnumerableオブジェクトを2つ同時にForEachで処理するための列挙子を生成する拡張メソッド
	/// </summary>
	/// <param name="stringTuple">対象とするstring型のIEnumerableオブジェクトのタプル</param>
	/// <returns></returns>
	public static IEnumerator<(string, string)> GetEnumerator(this (IEnumerable<string>, IEnumerable<string>) stringTuple)
	{
		var str1Enumerator = stringTuple.Item1.GetEnumerator();
		var str2Enumerator = stringTuple.Item2.GetEnumerator();

		// 要素数が少ないほうに合わせて列挙子を返す
		while (str1Enumerator.MoveNext() && str2Enumerator.MoveNext())
		{
			yield return (str1Enumerator.Current, str2Enumerator.Current);
		}
	}
}

上では、string型のIEnumerableオブジェクトをまとめたタプルに対してforeachで処理をするための列挙子をGetEnumerator拡張メソッドで提供しています。
定義したGetEnumerator拡張メソッドは、string型のIEnumerableオブジェクトをまとめたタプルに対してforeachで反復処理をする際に自動で呼び出されます。
そのため、以下のように明示的に拡張メソッドを呼び出さなくても処理ができます。

// ページ名とユーザーIDの組み合わせ一覧に対して反復処理をする
// (pageNames, userIds)のタプルに対してGetEnumeratorメソッドは自動で呼び出される
foreach (var (pageName, userId) in (pageNames, userIds))
{
    // ページ名
    var targetPageName = pageName;
    // ユーザーID
    var targetUserId = userId;
    ....
}

このGetEnumerator拡張メソッドは、独自のデータ型に対して独自の列挙子の取得処理を手軽に定義し、それを共有する際に使えるかなと思いました。
ただ、あまり安易に定義してしまうとデータ型によってforeachの振る舞いが想定と異なる動きをしてしまうという問題につながる可能性もあるため、使いどころを考える必要がありそうです。

まとめ

本記事ではGetEnumerator拡張メソッドを使って複数のIEnumerableオブジェクトをまとめてforeachで処理する方法をまとめました。
GetEnumerator拡張メソッドを使うと独自のデータ型に対して独自の列挙子を取得する処理が手軽に定義できることがわかりました。
ただ、安易に使うとデータ型によってforeachの振る舞いが想定と異なる動きをしてしまう危険性もあるため、使いどころを選んで使っていきたいと思いました。

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?