简体   繁体   English

如何评估System.Linq.Expressions.Expression

[英]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.

相关问题 如何使用System.Linq.Expressions.Expression根据子项进行过滤? - How to use System.Linq.Expressions.Expression to filter based on children? 如何为Like创建System.Linq.Expressions.Expression? - How to create a System.Linq.Expressions.Expression for Like? 如何从System.Linq.Expressions.Expression中删除括号? - How to remove Parantheses from System.Linq.Expressions.Expression? System.Linq.Expressions.Expression for .OrderBy函数 - System.Linq.Expressions.Expression for .OrderBy function 获取System.Linq.Expressions.Expression对象的类型 - Get type of System.Linq.Expressions.Expression object 将In和NotIn添加到System.Linq.Expressions.Expression中,是否可能? - Adding In and NotIn to System.Linq.Expressions.Expression, is it possible? IQStreamable:无法从“字符串”转换为“ System.Linq.Expressions.Expression” - IQStreamable: cannot convert from 'string' to 'System.Linq.Expressions.Expression C# lambda 返回 System.Linq.Expressions.Expression 的问题 - Issue with C# lambda return System.Linq.Expressions.Expression 什么是System.Linq.Expressions.Expression中的TKey <Func<TSource,TKey> &gt;? - What is TKey in System.Linq.Expressions.Expression<Func<TSource,TKey>>? 如何克服两个命名空间System.Linq.Expressions.Expression中的相同类型 <TDelegate> ? - How to overcome same type in two namespaces, System.Linq.Expressions.Expression<TDelegate>?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM