インスタンスメソッド,this, デリゲートへの束縛可能性
インスタンスメソッドの型は何?
public class Foo { public string Name { get; set; } public string GetStr(int i) { if( this != null ) return string.Format("My name is {0}", this.Name); else return string.Format("this is null!"); } }
こういうクラスの Foo.GetStr の型を考えてみる.
ソースコード上で引数はひとつでも,呼び出し時には型 Foo の暗黙の引数 this が最初の引数として存在する.
実際,.NET の Common Type System (CTS) はこいつに対応する MethodInfo から,Func<Foo, int, string> へのバインドを許している.
using System; static class Program { static void Main(string[] args) { var foo = new Foo { Name = "tekitou" }; Func<Foo, int, string> f1 = (_this, i) => _this.GetStr(i); // no exception Console.WriteLine(f1(foo, 1)); var mi = typeof(Foo).GetMethod("GetStr"); var f2 = Delegate.CreateDelegate(typeof(Func<Foo, int, string>), mi) as Func<Foo, int, string>; // no exception Console.WriteLine(f2(foo, 1)); } }
My name is tekitou My name is tekitou
this が null という可能性
this が null という状況を作り出したいとき,実は上記の方法が使える.
using System; static class Program { static void Main(string[] args) { Func<Foo, int, string> f1 = (_this, i) => _this.GetStr(i); // NullReferenceException // Console.WriteLine(f1(null, 1)); var mi = typeof(Foo).GetMethod("GetStr"); var f2 = Delegate.CreateDelegate(typeof(Func<Foo, int, string>), mi) as Func<Foo, int, string>; // no exception Console.WriteLine(f2(null, 1)); } }
this is null!
仮想メソッドではどうか?
CTS の面白いところは,基底クラスの仮想メソッドから,第 0 引数を「ほどいた」デリゲートを作成したときに,仮想メソッドのセマンティクスがちゃんと維持されるところ.もちろんこのとき this は null にできない.
using System; public class Foo { public override string ToString() { return "I'm Foo"; } } static class Program { static void Main(string[] args) { Func<object, string> toString1 = o => o.ToString(); Console.WriteLine(toString1(new object())); Console.WriteLine(toString1(new Foo())); var mi = typeof(object).GetMethod("ToString"); var toString2 = Delegate.CreateDelegate(typeof(Func<object, string>), mi) as Func<object, string>; Console.WriteLine(toString2(new object())); Console.WriteLine(toString2(new Foo())); // NullReferenceException // Console.WriteLine(toString2(null)); } }
System.Object I'm Foo System.Object I'm Foo