如果您的目标是从表达式中编译 + 调用“提取值”,那么您可能会寻找另一种方式。
我试图从表达式树中提取值而不通过反射进行编译。
我的解决方案并不完全支持所有表达式,起初它是为没有 lambda 和算术的缓存方法调用而编写的,但通过一些改进它可以提供帮助。
这里是:
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() 的更快替代方案