c# - 创建 FieldExpression 而不是 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)ConstantExpression
在exp1
示例中是一个,我们可以使用Expression.Constant()
函数创建它,但我需要以某种方式创建第二个表达式(exp2)
,该值不仅仅是一个常量,并且具有对局部变量的引用。该值为FieldExpression
Inexp2
示例。
主要目标是exp2
使用 Roslyn API 和表达式创建,我不知道我应该如何准确地传递值来实现这一点。
要创建,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);
}
如果你用你通过反射获得的 a 替换了,你应该有一些可以编译的东西FieldInfo.GetFieldFromHandle((RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/)
。FieldInfo
可以看到C#生成了一个新的类,这样局部变量就可以存放在那里而不是栈上。
此类的实例作为常量传递到表达式中,然后访问该类的字段。
我还发现让 C# 创建一个模板表达式,然后定义一个ExpressionVisitor
以将该表达式的一小部分与其他内容交换,这很有用。
例如,您可以编写一个将上述表达式中的每个参数实例与ofExpressionVisitor
交换以创建一个新的 lambda。无需担心 value 参数是如何提供的。f
Body
Body
l => l.Name
推荐阅读
- xamarin.forms - 使用 PushModalAsync 显示页面时无法显示警报控制器
- c++ - 如何使用 C++ 中的函数访问全局变量?
- flutter - 在 Flutter 中使用 Google 登录的错误 403 受限客户端
- node.js - 在nodejs中转义json中的双引号
- c# - 如何在asp.net后面的代码中的html按钮中获取隐藏字段值
- javascript - 无法将数据从视图发布到 Yii2 中的控制器
- postgresql - postgres 数据库中的这种编码是什么,我该如何解码
- android - Kotlin 数据绑定问题类型不匹配
- python - 如何修复 Pandas 中的 ValueError
- flutter - 颤振医生未处理的异常