创建 FieldExpression 而不是 ConstantExpression

Create a FieldExpression instead of ConstantExpression

解释起来有点棘手,所以我从一个例子开始。

     class Program
    {
        static void Main(string[] args)
        {
            var lst = new List<Test>();
            var value = "John";
            var exp1 = lst.AsQueryable().Where(l => l.Name == "John").Expression as MethodCallExpression;
            var exp2 = lst.AsQueryable().Where(l => l.Name == value).Expression as MethodCallExpression;

            Console.WriteLine(exp1.Arguments.Last().ToString()); // l => (l.name == "John")
            Console.WriteLine(exp2.Arguments.Last().ToString()); // l => (l.name == value(test1.Program+<>c__DisplayClass0_0).value)
        }
    }
    class Test
    {
        public string Name { get; set; }
    }

名称值 (John) 是 exp1 示例中的 ConstantExpression,我们可以使用 Expression.Constant() 函数创建它,但我需要以某种方式创建第二个表达式 (exp2),这个值不仅仅是一个常量,而且有一个局部变量的引用。 FieldExpression In exp2 示例。

主要目标是使用 Roslyn API 和表达式创建 exp2,我不知道我应该如何准确传递值才能实现。

要创建 exp1 我们可以这样做:

    var parameter = Expression.Parameter(typeof(Test), "l");
    var member = Expression.PropertyOrField( parameter , "Name");
    var valueExp = Expression.Constant(value);
    var exp1 = Expression.Equal(member, valueExp);

如何创建 exp2 !?

我发现使用反编译器查看 C# 如何实现此表达式会有所帮助;

string value = "Foo";
Expression<Func<string,bool>> test = f => f == value;

Becomes;

    private sealed class <>c__DisplayClass0_0
    {
        public string value;
    }

    public void M()
    {
        <>c__DisplayClass0_0 <>c__DisplayClass0_ = new <>c__DisplayClass0_0();
        <>c__DisplayClass0_.value = "Foo";
        ParameterExpression parameterExpression = Expression.Parameter(typeof(string), "f");
        BinaryExpression body = Expression.Equal(parameterExpression, Expression.Field(Expression.Constant(<>c__DisplayClass0_, typeof(<>c__DisplayClass0_0)), FieldInfo.GetFieldFromHandle((RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/)));
        ParameterExpression[] array = new ParameterExpression[1];
        array[0] = parameterExpression;
        Expression<Func<string, bool>> expression = Expression.Lambda<Func<string, bool>>(body, array);
    }

如果您将 FieldInfo.GetFieldFromHandle((RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/) 替换为通过反射获得的 FieldInfo,您应该可以编译。

可以看到C#生成了一个新的class,这样局部变量就可以存放在那里而不是在栈上

此 class 的实例作为常量传递到表达式中,然后访问 class 的字段。

我还发现让 C# 创建一个模板表达式很有用,然后定义一个 ExpressionVisitor 将该表达式的一小部分与其他内容交换。

例如,您可以编写一个 ExpressionVisitor 来交换上述表达式 Body 中参数 f 的每个实例,以及 l => l.NameBody =] 创建一个新的 lambda。无需担心 value 参数的确切提供方式。