前回、C#の配列にForEachメソッドを追加すると同じく、List
具体的にどういう状況か以下のコード(もしくは先述の記事)を参照。
public static void Main(string[] args) { var list = new List<string>() { "a", "b", "c", "d" }; list.ForEach(str => str + "_asdf"); // > a, b, c, d, // ★★★List.ForEachはTが構造体だと変更が作用しない }
拡張メソッドで配列にForeachを追加する
新しく、ListExtensionクラスを作成して操作を追加します。
// Listを拡張するためのクラス public static class ListExtension { // (1) refを使った構造体への作用の反映 public static void ForEach<T>(this IList<T> list, RefAction<T> action) { if (action == null) { throw new ArgumentNullException(nameof(action)); } for (int i = 0; i < list.Count; i++) { // インデクサはref渡せない T item = list[i]; action?.Invoke(ref item); list[i] = item; } } // (2) ラムダ式の戻り値を使用して作用を反映する public static void ForEach<T>(this IList<T> list, Func<T, T> action) { if (action == null) { throw new ArgumentNullException(nameof(action)); } for (int i = 0; i < list.Count; i++) { list[i] = action(list[i]); } } } // この宣言も同じところに追加 public delegate void RefAction<T>(ref T item);
使い方は以下の通り。
public static void Main(string[] args) { list.ForEach((ref string ss) => ss += "_asdf"); // 中身: a_asdf, b_asdf, c_asdf, d_asdf, // ★★★refを使うラムダ式で変更が反映される! list.ForEach(ss => { return ss + "_xyz"; }); // 中身: a_asdf_asdf_xyz, b_asdf_asdf_xyz, c_asdf_asdf_xyz, d_asdf_asdf_xyz, // ★★★戻り値を指定する版でも値が反映される! }
これで、Listが構造体や組み込み型のintやdoubleでも変更が反映されるようになりました。