あなたの目標が式から「値を抽出する」ためにコンパイル+呼び出すことである場合、別の方法を検討することができます。
リフレクションによってコンパイルせずに式ツリーから値を抽出しようとしています。
私の解決策は、すべての式を完全にサポートしているわけではありません。最初は、ラムダと算術演算を使用せずにメソッド呼び出しをキャッシュするために作成されましたが、いくつかの改善により役立ちます。
ここにあります:
private static object ExtractValue(Expression expression, object[] input, ReadOnlyCollection<ParameterExpression> parameters)
{
if (expression == null)
{
return null;
}
var ce = expression as ConstantExpression;
if (ce != null)
{
return ce.Value;
}
var pe = expression as ParameterExpression;
if (pe != null)
{
return input[parameters.IndexOf(pe)];
}
var ma = expression as MemberExpression;
if (ma != null)
{
var se = ma.Expression;
object val = null;
if (se != null)
{
val = ExtractValue(se, input, parameters);
}
var fi = ma.Member as FieldInfo;
if (fi != null)
{
return fi.GetValue(val);
}
else
{
var pi = ma.Member as PropertyInfo;
if (pi != null)
{
return pi.GetValue(val);
}
}
}
var mce = expression as MethodCallExpression;
if (mce != null)
{
return mce.Method.Invoke(ExtractValue(mce.Object, input, parameters), mce.Arguments.Select(a => ExtractValue(a, input, parameters)).ToArray());
}
var sbe = expression as BinaryExpression;
if (sbe != null)
{
var left = ExtractValue(sbe.Left, input, parameters);
var right = ExtractValue(sbe.Right, input, parameters);
// TODO: check for other types and operands
if (sbe.NodeType == ExpressionType.Add)
{
if (left is int && right is int)
{
return (int) left + (int) right;
}
}
throw new NotImplementedException();
}
var le = expression as LambdaExpression;
if (le != null)
{
return ExtractValue(le.Body, input, le.Parameters);
}
// TODO: Check for other expression types
var dynamicInvoke = Expression.Lambda(expression).Compile().DynamicInvoke();
return dynamicInvoke;
}
使用法:
private static string ToString<T>(Expression<Func<T, string>> expression, T input)
{
var sw = Stopwatch.StartNew();
var method = expression.Compile();
var invoke = method.Invoke(input);
sw.Stop();
Console.WriteLine("Compile + Invoke: {0}, {1} ms", invoke, sw.Elapsed.TotalMilliseconds);
sw.Restart();
var r2 = ExtractValue(expression, new object[] {input}, null);
sw.Stop();
Console.WriteLine("ExtractValue: {0}, {1} ms", r2, sw.Elapsed.TotalMilliseconds);
return invoke;
}
いくつかの改善と追加の式タイプにより、このソリューションは Compile().DynamicInvoke() のより高速な代替手段になる可能性があると思います