表达式树
lamda
表达式也可以赋给Expression<T>
类型
- LINQ to Object中,扩展方法的参数可以是:
- 委托类型
- 例如
Enumerable
类里的Where()
扩展方法: public static IEnumerable<TSource> Where(this IEnumerable<TSource> source, Func<TSource, bool> predicate);
- 委托类型是:
Func<TSource, bool>
- 例如
Expression<T>
- 例如
Queryable<T>
类定义的Where()
扩展方法: public static IQueryable<TSource> Where(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate);
Expression<T>
类型是:Expression<Func<TSource, bool>>
- 例如
- 委托类型
lamda
表达式可以作为值赋给- 委托类型
Expression<T>
c# 编译器可以根据参数类型是委托类型还是
Expression<T>
,对lamda
表达式做不同的事
- 当参数类型是
Expression<T>
时:- 编译器就从
lamda
表达式中创建一个表达式树 - 并将表达式树存储在程序集中
- 这样,就可以在运行期间分析表达式树
- 来做一个用于底层数据源的优化查询
- 编译器就从
表达式树在派生自抽象基类Expression的类中创建
- 其中,派生自抽象基类Expression的表达式类有:
- BinaryExpression
- ConstantExpression
- InvocationExpression
- LamdaExpression
- NewExpression
- NewArrayExpression
- TernaryExpression
- UnaryExpression
使用
Expression<T>
的例子有:Entity Framework Core和 WCF 数据服务的客户端提供程序
- 它们用
Expression<T>
参数定义了扩展方法 - 这样,访问数据库的LINQ程序,就可以读取表达式,创建一个运行期间优化的查询
using DataLib;
using System;
using System.Linq.Expressions;
namespace ExpressionTreeSample
{
class Program
{
static void Main()
{
Expression<Func<Racer, bool>> expression = r => r.Country == "Brazil" && r.Wins > 6;
DisplayTree(0, "Lambda", expression);
}
static void DisplayTree(int indent, string message, Expression expression)
{
string output = $"{ string.Empty.PadLeft(indent, '>')} {message} ! NodeType: {expression.NodeType}; Expr: {expression}";
indent++;
switch (expression.NodeType)
{
case ExpressionType.Lambda:
Console.WriteLine(output);
LambdaExpression lambdaExpr = (LambdaExpression)expression;
foreach (var parameter in lambdaExpr.Parameters)
{
DisplayTree(indent, "Parameter", parameter);
}
DisplayTree(indent, "Body", lambdaExpr.Body);
break;
case ExpressionType.Constant:
ConstantExpression constExpr = (ConstantExpression)expression;
Console.WriteLine($"{output} Const Value: {constExpr.Value}");
break;
case ExpressionType.Parameter:
ParameterExpression paramExpr = (ParameterExpression)expression;
Console.WriteLine($"{output} Param Type: {paramExpr.Type.Name}");
break;
case ExpressionType.Equal:
case ExpressionType.AndAlso:
case ExpressionType.GreaterThan:
BinaryExpression binExpr = (BinaryExpression)expression;
if (binExpr.Method != null)
{
Console.WriteLine($"{output} Method: {binExpr.Method.Name}");
}
else
{
Console.WriteLine(output);
}
DisplayTree(indent, "Left", binExpr.Left);
DisplayTree(indent, "Right", binExpr.Right);
break;
case ExpressionType.MemberAccess:
MemberExpression memberExpr = (MemberExpression)expression;
Console.WriteLine($"{output} Member Name: {memberExpr.Member.Name}, Type: {memberExpr.Type.Name}");
DisplayTree(indent, "Member Expr", memberExpr.Expression);
break;
default:
Console.WriteLine();
Console.WriteLine($"{expression.NodeType} {expression.Type.Name}");
break;
}
}
}
}
Lambda ! NodeType: Lambda; Expr: r => ((r.Country == "Brazil") AndAlso (r.Wins > 6))
> Parameter ! NodeType: Parameter; Expr: r Param Type: Racer
> Body ! NodeType: AndAlso; Expr: ((r.Country == "Brazil") AndAlso (r.Wins > 6))
>> Left ! NodeType: Equal; Expr: (r.Country == "Brazil") Method: op_Equality
>>> Left ! NodeType: MemberAccess; Expr: r.Country Member Name: Country, Type: String
>>>> Member Expr ! NodeType: Parameter; Expr: r Param Type: Racer
>>> Right ! NodeType: Constant; Expr: "Brazil" Const Value: Brazil
>> Right ! NodeType: GreaterThan; Expr: (r.Wins > 6)
>>> Left ! NodeType: MemberAccess; Expr: r.Wins Member Name: Wins, Type: Int32
>>>> Member Expr ! NodeType: Parameter; Expr: r Param Type: Racer
>>> Right ! NodeType: Constant; Expr: 6 Const Value: 6