読者です 読者をやめる 読者になる 読者になる

関数の呼び出し方

関数はどうやって実行できるのか調べてみた
前にも調べたような気がするけど、忘れたのでもう一回


C#でInt->String->Intの関数を呼ぶ場合

delegate int F_dele(String s);
static int f(String s) {return(999);}
var x = new F_dele(f);
Console.Write(x("Hoge"));

C#様に任せると大体こうなる
staticな関数だとF_dele.ctorの第一引数はldnullらしい

ldnull
ldftn int32 f(string)
newobj instance void F_dele.ctor(object, native int)
stloc x

ldloc x
ldstr "Hoge"
callvirt instance int32 F_dele::Invoke(string)
call void Console::WriteLine(int32)


delegateはMulticastDelegateを継承したクラスになるが、正直よくわかんない
呼び出したい型ごとにクラス作って、ldftnした結果をいれるのはじゃんくさい
ldftnした結果の型を管理を自分でやるならこれで十分

ldftn int32 f(string)
stloc x

ldstr "Hoge"
ldloc x
calli int32 (string)
call void Console::WriteLine(int32)


C#でInt->String->Intのメソッドを呼ぶ場合

delegate int F_dele(String s);
virtual int f(String s) {return(999);}
var self = new Self();
var x = new F_dele(self.f);
Console.Write(x("Hoge"));

メソッドだとF_dele.ctorの第一引数はレシーバっぽい
virtualじゃないメソッドなら当然ldftnになる

newobj Self::.ctor
stloc self

ldloc self
ldvirtftn int32 f(string)
newobj instance void F_dele.ctor(object, native int)
stloc x

ldloc x
ldstr "Hoge"
callvirt instance int32 F_dele::Invoke(string)
call void Console::WriteLine(int32)


メソッドをldftnするとcalliの第一引数をレシーバにせんとならん
ってことはILに落とす前にこれは関数orメソッドだかんねと言う情報をもたせんとならん
ついでにメソッド高階関数にすんのがめんどい

そのせいかldftnをdelegateでラップして渡すのがC#様やVB様のやり方くさい
デリゲートなら定義するときにldftn/ldvirtftnを切り替えてレシーバ渡すだけになる
関数を呼ぶところはcallvirtに統一できる