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

空談録

http://artfulplace.net/blogs/ からひっこしつつ

Expression Treesでparams引数のメソッドを呼び出す

後日死んだときにググってもいいけど書いたほうが早い

というわけで生きている気力が不足気味なのでオレオレクエリいじってます
なかなかクールな実装がされてていくらでも落ちそう

今回はstring.Formatのようにparamsで定義されてるメソッドの呼び出しについて考えます

string.Formatについて

string.Formatもparamsで定義されているー というイメージが強いのですが、実は5個オーバーロードがあり、そのうちの2つは3引数(string, object, object)と4引数(string, object, object, object)という形で、Expressionで扱う際にもformat引数を除いて3引数までは、他のメソッドと同様にExpression.Callをすればよいです
しかし変更する部分の引数が4を超えてくると通常のExpression.Callのように Expression.Call(typeof(string), "Format", null, args); としてargsにExpressionを5個とか入れても、該当するメソッドがないとしてInvalidOperationExceptionが返ってきます
これ自体は当然で、paramsはC#コンパイラでは認識していろいろしてくれますが、ExpresionとかReflectionで見るときにはparamsというものはなく、(string, object)だけで見られます
なので第2引数以下をobject
に変換してあげれば動きます

Expression → object

という理論的には簡単なのですがただExpressionを作っても動きません
Expression
で渡した場合、動作しますがExpression.ToStringの値が渡るためほぼ動作してないといえます

で、適当に検索すると
c# - Calling (params object[]) with Expression[] - Stack Overflow とか出てくるので見てみると、Expression.NewArrayInitを使えと書いてあります

というわけでExpression.NewArrayInitで実装してみるとこんな感じ

// Expression fmt;
// Expression[] exprs;


var stfmt = typeof(string).GetRuntimeMethod("Format", new Type[] { typeof(string), typeof(object[]) });
var args = Expression.NewArrayInit(typeof(string),exprs);
var stfmtexpr = Expression.Call(stfmt, new Expression[] { fmt, args});

これでexprsにstring.Formatで与えたいメンバーなり値を配列で入れておいて実行するだけです

ちゃんと知りたい場合はStackOverflowのほうを見たほうがいいと思われます


オレオレクエリの実装が魔界すぎていじってると背後からバグに殴られて死にます
もうちょっとまともにしたい

この辺で