[英]How to evaluate a System.Linq.Expressions.Expression
评估System.Linq.Expressions.Expression
以获取值(对象)的正确或可靠方法是什么?
I'm tentatively using the following, but don't know if it's the preferred method: 我暂时使用以下内容,但不知道它是否是首选方法:
public static object Evaluate(Expression e)
{
//A little optimization for constant expressions
if (e.NodeType == ExpressionType.Constant)
return ((ConstantExpression)e).Value;
return Expression.Lambda(e).Compile().DynamicInvoke();
}
Timothy Shields answer is correct for when there are no parameters. 当没有参数时,Timothy Shields的回答是正确的。 For parameterized expressions, you can use an overload of Expression.Lambda
(the method used in his code) that takes a collection of ParameterExpression
, but the ParameterExpression
values have to be the same instances as those used in the given Expression
. 对于参数化表达式,您可以使用Expression.Lambda
(在其代码中使用的方法)的重载,该重载采用ParameterExpression
的集合,但ParameterExpression
值必须与给定Expression
使用的实例相同。 If it is a sub-expression of a parameterized expression using parameters from the root expression, you can get the parameters from it (pass LambdaExpression.Parameters
from the root to Expression.Lambda
). 如果它是使用根表达式中的参数的参数化表达式的子表达式,则可以从中获取参数(将LambdaExpression.Parameters
从根传递到Expression.Lambda
)。
(If your expression is already a LambdaExpression
, you can just cast to it and call Compile
.) (如果你的表达式已经是LambdaExpression
,你可以转换为它并调用Compile
。)
Then pass your parameters to DynamicInvoke(...)
. 然后将您的参数传递给DynamicInvoke(...)
。
Here's a method, extending Timothy Shields method, for invoking a sub-expression when you have the root expression. 这是一个扩展Timothy Shields方法的方法,用于在有根表达式时调用子表达式。 The root expression must be LamdbaExpression
(or a subclass such as Expression<TDelegate>
.) 根表达式必须是LamdbaExpression
(或子类,如Expression<TDelegate>
。)
Since we don't know which parameters of the root expression are required by the sub-expression, we pass all of them. 由于我们不知道子表达式需要根表达式的哪些参数,因此我们传递了所有这些参数。 (If you know this in your case, you can adapt this code.) (如果您知道这种情况,可以调整此代码。)
This doesn't handle all cases. 这并不能处理所有情况。 It doesn't let you get the values of out
or reference parameters, and there are probably other unsupported cases. 它不允许您获取out
或reference参数的值,并且可能存在其他不受支持的情况。
If your expression is not a sub-expression or you don't have the root Expression
, you'll have to get the ParameterExpression
s some other way. 如果您的表达式不是子表达式,或者您没有根Expression
,则必须以其他方式获取ParameterExpression
。
using System;
using System.Linq;
using System.Linq.Expressions;
namespace LinqTest
{
public class LinqCompileSubExpression
{
/// <summary>
/// Compile and invoke a sub-expression of a <see cref="LambdaExpression"/>.
/// </summary>
/// <param name="rootExpression">A parameterised expression.</param>
/// <param name="subExpression">An sub-expression or <paramref name="rootExpression"/>.</param>
/// <param name="arguments">
/// The arguments to be supplied on invoking. These must match the parameters to the root expression (empty if it has no parameters).
/// Any parameters not used by the sub-expression are ignored.
/// </param>
/// <returns>The return value of the sub-expression.</returns>
/// <typeparam name="TReturn">The type of the return value. Use <see cref="Object"/> if it is not known.</typeparam>
/// <remarks>
/// If invoking the same expression multiple times, use <see cref="CompileSubExpression(LambdaExpression, Expression)"/> once,
/// then invoke the delegate (for efficiency).
/// </remarks>
public static TReturn InvokeSubExpression<TReturn>(LambdaExpression rootExpression, Expression subExpression, params object[] arguments)
{
// compile it (to a delegate):
Delegate compiledDelegate = CompileSubExpression(rootExpression, subExpression);
// invoke the delegate:
return (TReturn)compiledDelegate.DynamicInvoke(arguments);
}
/// <summary>
/// Compile a sub-expression of a <see cref="LambdaExpression"/>.
/// </summary>
/// <param name="rootExpression">A parameterised expression.</param>
/// <param name="subExpression">An sub-expression or <paramref name="rootExpression"/>.</param>
/// <returns>The compiled expression.</returns>
public static Delegate CompileSubExpression(LambdaExpression rootExpression, Expression subExpression)
{
// convert the sub-expression to a LambdaExpression with the same parameters as the root expression:
LambdaExpression lambda = Expression.Lambda(subExpression, rootExpression.Parameters);
// compile it (to a delegate):
return lambda.Compile();
}
}
}
These are unit tests using the Microsoft test framework. 这些是使用Microsoft测试框架的单元测试。 The actual code required is three lines in the two static methods above. 所需的实际代码是上面两种静态方法中的三行。
using System;
using System.Linq;
using System.Linq.Expressions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using static LinqTest.LinqCompileSubExpression;
namespace LinqTest
{
[TestClass]
public class LinqCompileSubExpressionTest
{
[TestMethod]
public void InvokeExpressionTest1()
{
Expression<Func<string, int>> rootExpression = s => s.Substring(4).Length;
var subExpression = ((MemberExpression)rootExpression.Body).Expression; // just the `s.Substring(4)` part
Assert.AreEqual("t string", InvokeSubExpression<string>(rootExpression, subExpression, "input string"));
}
[TestMethod]
public void InvokeExpressionTest2()
{
Expression<Func<object, int>> rootExpression = x => x.ToString().Length;
var subExpression = ((MemberExpression)rootExpression.Body).Expression; // just the `x.ToString()` part
Assert.AreEqual("5", InvokeSubExpression<string>(rootExpression, subExpression, 5));
}
[TestMethod]
public void InvokeExpressionTest3()
{
Expression<Func<ClassForTest, int>> rootExpression = x => x.StrProperty.Length + 15;
var subExpression = ((BinaryExpression)rootExpression.Body).Right; // `15`
Assert.AreEqual(15, InvokeSubExpression<int>(rootExpression, subExpression, new ClassForTest())); // argument is irrelevant
}
[TestMethod]
public void InvokeExpressionTest4()
{
Expression<Func<int, int>> rootExpression = x => Math.Abs(x) + ClassForTest.GetLength(x.ToString());
var subExpression = ((BinaryExpression)rootExpression.Body).Right;
Assert.AreEqual(3, InvokeSubExpression<int>(rootExpression, subExpression, 123)); // we pass root parameter but evaluate the sub-expression only
}
[TestMethod]
public void InvokeExpressionTest5()
{
Expression<Func<int, int>> rootExpression = x => ClassForTest.GetLength(x.ToString());
var subExpression = ((MethodCallExpression)rootExpression.Body).Arguments[0]; // just the `x.ToString()` part
Assert.AreEqual("123", InvokeSubExpression<string>(rootExpression, subExpression, 123)); // we pass root parameter but evaluate the sub-expression only
}
public class ClassForTest
{
public string StrProperty { get; set; }
public static int GetLength(string s) => s.Length;
}
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.