拡張メソッドを null に対して呼び出すと...
以前の記事で 拡張メソッドについて説明しました。
拡張メソッドはスタティックメソッドでありながら、変数に対して . (ドット) を使って、すなわちインスタンスメソッドのように呼び出すことが可能です。
では、null がセットされている変数 (オブジェクト) に対して拡張メソッドを呼び出すとどうなるでしょうか。
念のために実験して動作確認しておきましょう。
拡張メソッドは null に対しても安全
それでは実験のために次のような、string 型に対する拡張メソッドを用意します。
using System;
namespace MyTest
{
public static class Extensions
{
public static void Print(this string s)
{
if (string.IsNullOrEmpty(s))
{
Console.WriteLine("(null)");
}
else
{
Console.WriteLine("{0}", s);
}
}
}
}
string に対する拡張で、コンソールに string を出力します。
ここでこの拡張メソッドを、次のプログラムで実験します。
namespace MyTest
{
class Program
{
static void Main(string[] args)
{
string s1 = "Hello";
s1.Print();
string s2 = null;
s2.Print();
string s3 = null;
s3.ToString();
}
}
}
ここでは string として s1, s2, s3 をそれぞれ用意し、s1 には "Hello" という文字列で初期化、s2, s3 には null で初期化します。
s1, s2 は拡張メソッドである Print() を呼び出し、s3 はインスタンスメソッドとして ToString() を呼び出します。
この結果は次のようになりました。
Hello (null) Unhandled Exception: System.NullReferenceException: Object reference not set to an instance of an object. at MyTest.Program.Main(String[] args) in C:\...
インスタンスメソッドは、null に対して呼び出せば当然ながら NullReferenceException が発生する。 しかし、拡張メソッドは null に対しても安全に呼び出せる、ということが確認できました。
念のため IL コードもみておきます。
ILDasm で IL を覗くと、次のようになります。
.method private hidebysig static void Main(string[] args) cil managed { .entrypoint // Code size 33 (0x21) .maxstack 1 .locals init ([0] string s1, [1] string s2, [2] string s3) IL_0000: nop IL_0001: ldstr "Hello" IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: call void MyTest.Extensions::Print(string) IL_000d: nop IL_000e: ldnull IL_000f: stloc.1 IL_0010: ldloc.1 IL_0011: call void MyTest.Extensions::Print(string) IL_0016: nop IL_0017: ldnull IL_0018: stloc.2 IL_0019: ldloc.2 IL_001a: callvirt instance string [mscorlib]System.Object::ToString() IL_001f: pop IL_0020: ret } // end of method Program::Main
つまり拡張メソッドはドット形式ではありますが、内部でスタティックメソッドの呼び出しに展開されています。
このために null に対しても問題なく呼び出せるわけです。