前章まで、戻り値の型をどのコレクションインタフェースにするかによって、クライアントに公開する情報を制御できる、という話をしてきました。この章では、まずその内容を短くまとめたうえで、IEnumerable<T>に用意されているLINQの拡張メソッドについて補足します。
ここまでのまとめ
クライアントに公開する情報は、コレクションインタフェースで制御します。まとめると次のようになります。
図1 3つのコレクションインタフェースの使い分けをコメントにまとめる
リスト1 コレクションインタフェースの使い分け(Form1.cs)
var b = Products.GetDataB(); //読むだけ → IEnumerable<T> //追加・削除したい → ICollection<T> //インデックス指定も必要 → IList<T>
読むだけでよければIEnumerable<T>、追加や削除もしたいのであればICollection<T>、さらにインデクサ、つまりインデックス指定もしたい場合はIList<T>を使えばよい、ということです。
IEnumerable<T>にはLINQの拡張メソッドがある
ちなみに、IEnumerable<T>にはLINQの拡張メソッドというものがあります。
変数bは、Products.GetDataBの戻り値なのでIEnumerable<string>型です。前章では「これは基本的に何にも使えません」という話をしました。b.と入力したときに表示されるのは、Equals、GetHashCode、GetType、ToStringというobject系のメソッドと、GetEnumeratorだけでした。
図2 IEnumerable<T>自身のメンバーはobject系のメソッドとGetEnumeratorだけ
ところが、IntelliSenseのフィルターで拡張メソッドも表示するようにすると、いろいろなメソッドが出てきます。Aggregate、All、Any、Append……と、ずらっと並んでいます。
図3 拡張メソッドを表示すると、AllやAnyなど多数のメソッドが現れる
図4 スクロールするとGroupBy、Join、Lastなども並んでいる
IEnumerable<T>インタフェース自体に定義されているメンバーはGetEnumeratorだけなのに、なぜこれだけのメソッドが使えるのでしょうか。
拡張メソッドはEnumerableクラスに書かれている
これは何かというと、Enumerableというクラスに拡張メソッドが書かれているのです。Anyの定義に移動して、実際のソースコードを見てみます。
図5 System.Linq名前空間にあるstaticなEnumerableクラス
System.Linq名前空間に、staticのEnumerableというクラスがあります。そして、その中のメソッドは、第1引数がthis IEnumerable<TSource>型になっています。
図6 Anyメソッドの第1引数はthis IEnumerable<TSource>型
リスト2 EnumerableクラスのAnyメソッド(System.Linq、抜粋)
namespace System.Linq
{
public static partial class Enumerable
{
public static bool Any<TSource>(this IEnumerable<TSource> source)
{
if (source is null)
{
ThrowHelper.ThrowArgumentNullException(
ExceptionArgument.source);
}
// …(要素が1件でもあればtrueを返す処理)
}
}
}
staticクラスのstaticメソッドで、第1引数にthisを付けて書かれたものが拡張メソッドです。this IEnumerable<TSource>と書かれているので、このメソッドはIEnumerable<T>型の変数に対して、あたかもその型のインスタンスメソッドであるかのように呼び出すことができます。
リスト3 拡張メソッドは2通りの書き方で呼び出せる(参考)
IEnumerable<string> b = Products.GetDataB(); // 拡張メソッドとして呼び出す(普段の書き方) bool r1 = b.Any(); // 実体はEnumerableクラスのstaticメソッドなので、こう書いても同じ bool r2 = Enumerable.Any(b);
IEnumerable<T>型は、このEnumerableクラスにある機能が使えるので、基本的にLINQが使える、というわけです。
WhereやAnyが使える
クライアントコードに戻って確認してみます。b.whereと入力するとWhereが、b.anyと入力するとAnyが候補に出てきます。
図7 b.whereと入力するとWhereが候補に出る
図8 b.anyと入力するとAnyが候補に出る
IEnumerable<T>に対してWhereやAnyが使えて、そこにラムダ式が書けるのは、このEnumerableクラスの拡張メソッドのおかげということです。ラムダ式やLINQがよく分からないという方は、ラムダ式のコースを参照してください。
リスト4 IEnumerable<string>に対してLINQを使う(参考)
var b = Products.GetDataB(); // IEnumerable<string>
// 条件に合う要素だけを取り出す
var r1 = b.Where(x => x.StartsWith("ボ"));
// 条件に合う要素が1件でもあるか
var r2 = b.Any(x => x == "スパイク");
// 件数を数える
var r3 = b.Count();
b.Add("AAA")のように元のデータを変更する操作は相変わらずできません。読み取るだけという制限はそのままで、絞り込みや存在チェックといった問い合わせだけができるようになっています。
まとめ
図9 この章でまとめたコメント
リスト5 この章のまとめ(Form1.cs)
//読むだけ → IEnumerable<T> //追加・削除したい → ICollection<T> //インデックス指定も必要 → IList<T> //IEnumerable<T> は繰り返しだけだが、 //拡張メソッドがEnumerableクラスに書かれているため、 //LINQのメソッドが使える。これを使えるだけでも、 //コレクションインタフェースを使う価値がある。
IEnumerable<T>は基本的に繰り返しだけ、それしかできないと説明してきましたが、Enumerableクラスに書かれた拡張メソッドによって、WhereやAnyといったLINQのメソッドは使えます。これが使えるだけでも、コレクションインタフェースを使う価値があります。
■非公開コース「C#14新機能」プレゼント:
非公開コース「C#14新機能」(80分)をご覧になりたい方は
こちらからURLとパスワードを発行していますので、ご覧になってみてください。
非公開コース「C#14新機能」を観る
A01_はじめに
A02_プロジェクトの作成
B01_配列とは
B02_配列の生成とアクセス
B03_生成と同時に値を設定する
B04_型推論による生成
B05_メソッドの引数などにする場合の注意点
B06_Length
B07_IndexOfでの検索
B08_FindIndexでの検索
B09_Find
B10_Exists
B11_FindAllとFindLast
B12_誤ったコピー
B13_Array.Copy
B14_範囲指定のコピー
B15_Resize
C01_ArrayList
C02_List
C03_List 動的な要素の変更
C04_Listのコンストラクタ
C05_Listのコンストラクタ_Capacity
C06_ListTからArrayクラスのメソッドが呼ばれている
D01_コレクションインタフェースとは
D02_異なるコレクションクラスに互換性を持たせる
D03_インタフェースの階層構造
D04_クライアントができる操作を制限させる
D05_クライアントができる操作を制限させる_後半
D06_Enumerableの拡張メソッドに関して
D07_ReadOnly系のコレクションインタフェース
D08_AsReadOnly
D09_ToListでコピーする
D10_ListTはprivateで使う
■非公開コース「C#14新機能」プレゼント:
非公開コース「C#14新機能」(80分)をご覧になりたい方は
こちらからURLとパスワードを発行していますので、ご覧になってみてください。
非公開コース「C#14新機能」を観る








