创建 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;
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.Name
的 Body
=] 创建一个新的 lambda。无需担心 value 参数的确切提供方式。
解释起来有点棘手,所以我从一个例子开始。
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;
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.Name
的 Body
=] 创建一个新的 lambda。无需担心 value 参数的确切提供方式。