call と callvirt その3 : struct と callvirt
MSDNライブラリの Constrained の説明によると、「通常、callvirt 命令は値型では有効ではありません。」だそうだけど、普通に動いた。ILの世界はおおらかだ。
using System; using System.Reflection.Emit; struct Baz { private int n; public int Value { get { return n; } } public Baz( int n ) { this.n = n; } public override string ToString() { return "Baz: " + n; } } class Class5 { static void Main() { Baz baz = new Baz( 2 ); Console.WriteLine( " baz.Value: {0}", baz.Value ); // Valueプロパティを読む DynamicMethod dm = new DynamicMethod( "", typeof( int ), new Type[] { typeof( Baz ) }, typeof( Class5 ) ); ILGenerator ilgen = dm.GetILGenerator(); ilgen.Emit( OpCodes.Ldarga_S, ( byte ) 0 ); //ilgen.Emit( OpCodes.Call, typeof( Baz ).GetProperty( "Value" ).GetGetMethod() ); ilgen.Emit( OpCodes.Callvirt, typeof( Baz ).GetProperty( "Value" ).GetGetMethod() ); ilgen.Emit( OpCodes.Ret ); Console.WriteLine( dm.Invoke( null, new object[] { baz } ) ); // ToString() を呼ぶ DynamicMethod dm2 = new DynamicMethod( "", typeof( string ), new Type[] { typeof( Baz ) }, typeof( Class5 ) ); ILGenerator ilgen2 = dm2.GetILGenerator(); ilgen2.Emit( OpCodes.Ldarga_S, ( byte ) 0 ); //ilgen2.Emit( OpCodes.Call, typeof( Baz ).GetMethod( "ToString" ) ); ilgen2.Emit( OpCodes.Callvirt, typeof( Baz ).GetMethod( "ToString" ) ); ilgen2.Emit( OpCodes.Ret ); Console.WriteLine( dm2.Invoke( null, new object[] { baz } ) ); Console.ReadKey(); } }
この例では、call/callvirt共に正常に動く。
ただし、BazがToStringをオーバーライドしていないとヌルポになる。で、それを解決してくれるのが constrained。でも、残念ながら値型のインスタンスメソッド(非仮想メソッド)には対応してないみたい。値型の仮想メソッドには対応している。