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のほうを見たほうがいいと思われます
オレオレクエリの実装が魔界すぎていじってると背後からバグに殴られて死にます
もうちょっとまともにしたい
この辺で