今回は、型パラメータがある場合の拡張メソッドを見ていきます。まずは型パラメータのある拡張メソッドを従来の形で書いてから、これをextensionブロックに変える、という流れは次回に回し、今回は従来の書き方で仕上げるところまでを扱います。
既存の拡張メソッドを確認する
題材にするのは、これまでの回で作成したint型向けの拡張メソッドです。extensionブロックの中にIsZero、IsNegativeという2つのメソッドが定義されています。
図1: 既存のint型向け拡張メソッド(IsZero / IsNegative)
コレクション向けのIsEmptyメソッドを追加する
ここに、コレクションが空かどうかを判定するIsEmptyメソッドを追加します。対象はIEnumerable<T>とし、任意の型のコレクションに対応できるようにします。今回はextensionブロックの中ではなく、クラスの直下に従来どおりのstaticメソッドとして書いていきます。
最初はthisキーワードや型パラメータのTを付けずに書き始め、段階的に次のように変えていきます。
// ① まずは型パラメータなしで書き始める public static bool IsEmpty(IEnumerable<T> list) // ② thisを付けて拡張メソッドにする public static bool IsEmpty(this IEnumerable<T> list) // ③ メソッド名の直後に<T>を付けて型パラメータを宣言する public static bool IsEmpty<T>(this IEnumerable<T> list)
このように、使いたい型パラメータTをメソッド名の後ろに<T>という形で宣言することで、任意の型のコレクションに対応した拡張メソッドになります。
nullチェックを含めた実装
IsEmptyという名前のとおり、「値が空かどうか」を判定するメソッドなので、要素が0件のときだけでなく、コレクションそのものがnullのときもtrueを返すようにします。Any()は1件でも要素があればtrueを返すメソッドなので、その否定を使えば「要素がない」かどうかを判定できます。最終的な実装は次のとおりです。
public static bool IsEmpty<T>(this IEnumerable<T> list)
{
if (list == null) return true;
return !list.Any();
}
図2: 完成したIsEmpty<T>メソッド
listがnullのときにreturn trueとしている点がポイントです。拡張メソッドは静的メソッドとして呼び出されるため、たとえ変数がnullであっても例外にならず、その値がそのままlist引数としてメソッド内に渡ってきます。そのため、メソッドの中でこのようにnullチェックを行うことができます。
使用例で動作を確認する
実際に3パターンのList<string>を用意して、IsEmptyの戻り値を確認します。
var listA = new List<string>();
listA.Add("AAA");
var retA = listA.IsEmpty();
var listB = new List<string>();
var retB = listB.IsEmpty();
List<string> listC = null;
var retC = listC.IsEmpty();
図3: listA(要素あり)/listB(0件)/listC(null)を用意する
listAは要素を1件追加しているのでfalse(空ではない)、listBはnewしただけで0件なのでtrue、listCはnullなのでtrueになるはずです。デバッグ実行してウォッチウィンドウで値を確認すると、想定どおりの結果になっていることが分かります。
図4: retA = false、retB = true、retC = true
listCに対してlistC.IsEmpty()と呼び出している部分は、一見するとnull参照で例外になりそうに見えます。しかし実際には拡張メソッドの中を経由して呼び出されるため、IsEmptyメソッドの引数listにnullが渡ってくるだけで、呼び出し自体は例外になりません。そのメソッドの中でif (list == null) return true;というチェックが効いているため、正しくtrueが返っています。
クラス名をIntExからExへ変更する
このクラスはこれまでint型専用の拡張メソッドを格納する目的でIntExという名前にしていましたが、今回コレクションを対象とするメソッドを追加したことで、int専用ではなくなりました。そこでクラス名をExに変更しておきます。
図5: クラス名をIntExからExに変更
nullチェックがなければ例外になることを確認する
最後に、nullチェックが本当に効いているのかを確認するため、試しにif (list == null) return true;の行をコメントアウトしてから実行してみます。
図6: nullチェックをコメントアウトすると、listC.IsEmpty()の実行時にハンドルされていない例外(NullReferenceException)が発生する
この状態で実行すると、listC.Any()の呼び出しでハンドルされていない例外が発生します。コメントを元に戻して再度実行すると、例外にならずに正常に通ることが確認できます。これにより、null時にtrueを返すチェックが正しく機能していることが分かります。
図7: コメントを戻した最終的なIsEmpty<T>メソッド(3個の参照)
まとめ
今回は、型パラメータのある拡張メソッドを従来の書き方で作成しました。ポイントは、メソッド名の直後に<T>を付けて型パラメータを宣言すること、拡張メソッドは静的メソッド呼び出しとして解決されるためnullを渡しても例外にならないこと、そしてその性質を利用してメソッド内でnullチェックを行えることです。次回は、この拡張メソッドをextensionブロックの形に書き換えていきます。
Udemyでの動画版はこちらからご覧いただけます
00_はじめに
01_VisualStudio2026をインストールしてください
A01_fieldキーワード
B01_extensionブロックとは
B02_拡張メソッドとは
B03_extensionブロック
B04_型パラメータありの拡張メソッド
B05_型パラメータありのextensionブロックの書き方
B06_拡張プロパティ
B07_拡張静的プロパティ&メソッド
B08_拡張演算子
C01_partialコンストラクター
C02_幽霊コードを定義パーツで契約による実装をするという考え方
C03_partialイベント
D01_Null条件付き代入
E01_複合代入演算子のオーバーロード
F01_明示的な型を指定せずにラムダパラメータでパラメータ修飾子を使用する
G01_nameofはバインドされていないジェネリック型をサポートします
H01_SpanTおよびReadOnlySpanTのより暗黙的な変換
Udemyでの動画版はこちらからご覧いただけます