参考文章
1. 基本了解
1.1 Lambda表达式
演变过程
using System;
namespace lq1
{
class Program
{
public delegate void Tesk(int x);
public delegate int TeskPara(int x);
static void Main(string[] args)
{
new Program().Run();
}
public void Show(int x)
{
Console.WriteLine("Show");
}
public void Run()
{
// Lambda演变历史
{
// .net framework 1.0/1.1
Tesk tesk = new Tesk(this.Show);
tesk.Invoke(1);
}
int i = 0;
{
// .net framework 2.0,匿名方法,增加delegate关键字,可以访问局部变量
Tesk tesk = new Tesk(delegate (int x)
{
Console.WriteLine("Show" + i);
});
tesk.Invoke(2);
}
{
// .net framework 3.0 移除delegate关键字,增加 => 语法(goes to)
Tesk tesk = new Tesk((int x) =>
{
Console.WriteLine("Show" + i);
});
tesk.Invoke(3);
}
{
// 可以省略参数类型,参数类型根据委托自动推断
Tesk tesk = new Tesk((x) =>
{
Console.WriteLine("Show" + i);
});
tesk.Invoke(3);
}
{
// 只有一个参数或一行代码时省略括号
Tesk tesk = new Tesk(x => Console.WriteLine("Show" + i));
tesk.Invoke(3);
}
{
// 省略实例委托代码
Tesk tesk = x => Console.WriteLine("Show" + i);
tesk.Invoke(3);
}
{
// 有返回值且有一行代码时,可以直接写返回值(省略 return)
TeskPara tesk = x => x + 1;
tesk.Invoke(5);
Func<int, int> func = x => x + 2;
func.Invoke(5);
}
}
}
}
概述说明
Lambda
表达式是一个特殊的匿名函数,是一种高效的类似于函数式编程的表达式,简化开发中需要编写的代码量
可以包含表达式和语句,并且可用于创建委托或表达式目录树类型,支持带有可绑定到委托或表达式树的输入参数的内联表达式
所有Lambda
表达式都使用Lambda
运算符=>
,运算符的左边是输入参数(如果有),右边是表达式或语句块
Lambda
表达式不能在委托链中进行删(-=
)操作,因为每个表达式的名称都不一样
1.2 隐式类型
在C# 3.0中,引进了关键字叫做var
,var
允许声明一个新变量,它的类型是从用来初始化器变量的表达式里隐式的推断出来的,即在声明时,不需要给它定义类型,它会根据它的初始化器表达式来推断出它的类型
var
本身不是类型,而是向编译器发出一条用来推断和分配类型的指令,因此,隐式类型也叫推断类型,由编辑器自动根据表达式推断出对象的最终类型
隐式类型的本地变量是强类型变量(就好像您已经声明该类型一样),但由编译器确定类型
var Name = "李白";
var Age = 18;
var Interest = new List<string>{"唱歌","阅读"}
1.3 匿名类型
概述说明
字面意思:没有名字的类型
演变历史
public void Starting()
{
{
// 使用 object 声明
object model = new
{
id = 1,
name = "libai"
};
//Console.WriteLine(model.id); 强类型语言,编译器检测不到object类型中有id属性
}
{
// 使用 dynamic 声明,动态类型,避开编译器检查
dynamic model = new
{
id = 2,
name = "libai"
};
Console.WriteLine(model.id);
Console.WriteLine(model.age); // 运行时出错
}
{
// 使用 var 声明,匿名类型,自动推断,声明后的属性是只读的
var model = new
{
id = 3,
name = "libai"
};
Console.WriteLine(model.id);
//Console.WriteLine(model.age); 编译器检测(自动推断)无age字段
}
}
1.4 扩展方法
概述说明
扩展方法:允许在不修改类型的内部代码的情况下为类型添加独立的行为
扩展方法只能定义在 非泛型的静态类中,使用 static
修饰,参数使用this
关键字 修饰要扩展的类。就是说扩展方法的第一个参数必须是this
关键开头然后经跟要扩展的对象类型,然后是扩展对象在运行时的实例对象引用
扩展方法是一种特殊的静态方法,可以像扩展类型上的实例方法一样进行调用,能向现有类型“添加”方法,而无须创建新的派生类型、重新编译或以其他方式修改原始类型
在使用时编译器认为一个表达式要使用一个实例方法,但是没有找到,需要检查导入的命名空间和当前命名空间里所有的扩展方法,并匹配到适合的方法
示例一:简单定义
public static class Extend
{
public static int ToInt(this int? k)
{
return k ?? 0;
}
}
示例二:简单定义
public interface ILog
{
void log(string message,LogLevel logLevel);
}
public static class ILogExtensions
{
//记录调试信息
public static void LogDebug(this ILog logger,string message)
{
if(true) //判断日志配置中是否允许输入Debug类型的日志
{
logger?.Log($"{message}",LogLevel.Debug);
}
}
}
示例三:模拟
where
方法
using System;
using System.Collections.Generic;
namespace lq2
{
class Program
{
static void Main(string[] args)
{
List<User> list = new List<User>
{
new User(){uid=1,uname="a1",age=18,gender=0 },
new User(){uid=2,uname="a2",age=28,gender=1 },
new User(){uid=3,uname="a3",age=23,gender=1 },
new User(){uid=4,uname="a4",age=18,gender=0 },
new User(){uid=5,uname="a5",age=33,gender=1 }
};
var d1 = list.MyWhere(x => x.uid > 3);
Console.WriteLine(d1.Count);
var d2 = list.MyWhere(x => x.age >= 18 && x.gender == 0);
Console.WriteLine(d2.Count);
}
}
public class User
{
public int uid { get; set; }
public int age { get; set; }
public string uname { get; set; }
public int gender { get; set; }
}
public static class Extend
{
public static List<T> MyWhere<T>(this List<T> rouse, Func<T, bool> func)
{
List<T> list = new List<T>();
foreach (var item in rouse)
{
if (func(item))
{
list.Add(item);
}
}
return list;
}
}
}
2. 表达式目录树Expression
2.1 简单概述
表达式树,Expression
(System.Linq.Expressions
),是一种数据结构体,用于存储需要计算,运算的一种结构,这种结构可以只是存储,而不进行运算,或者说是描述不同变量和常用之间关系的一种数据结构
表达式目录树以数据形式表示语言级别代码,数据存储在树形结构中,目录树中的每个节点都表示一个表达式,简单的说是一种语法树,或者说是一种数据结构
表达式目录树不能有语句体,不能当作方法,不能有大括号,只能有一行代码
2.2 声明表达式目录树
第一种方式,快捷声明,用
Lambda
声明表达式目录树
示例一:普通类型
Expression<Func<int, int, int>> exp = (n, m) => n * m + 2;
示例二:实体类声明
Expression<Func<User, bool>> lambda = x => x.age > 18;
第二种方式,手动拼装目录树(原始方式),简单示例
namespace e1
{
using System;
using System.Linq.Expressions;
class Program
{
static void Main(string[] args)
{
// 以此表达式为例,手动拼接,实现相同作用
Expression<Func<int, int, int>> func = (x, y) => x * y + 2;
// 声明变量表达式
ParameterExpression px = Expression.Parameter(typeof(int), "x");
ParameterExpression py = Expression.Parameter(typeof(int), "y");
// 声明常量表达式
ConstantExpression constant = Expression.Constant(2, typeof(int));
// 声明乘积表达式
BinaryExpression multiply = Expression.Multiply(px, py);
// 声明相加表达式
BinaryExpression add = Expression.Add(multiply, constant);
// 声明参数表达式
ParameterExpression[] parameters = new ParameterExpression[] { px, py };
// 生成表达式目录树
Expression<Func<int, int, int>> exp = Expression.Lambda<Func<int, int, int>>(add, parameters);
// 表达式目录树生成委托
var ifunc = exp.Compile();
Console.WriteLine("委托结果:" + func.Compile().Invoke(1, 2));
Console.WriteLine("表达式树:" + ifunc.Invoke(1, 2));
}
}
}
2.3 表达式类型
方法 | 类型 | 描述 |
---|---|---|
Expression.Parameter(...) | ParameterExpression | 表示一个命名参数(变量)表达式 |
Expression.Constant(...) | ConstantExpression | 表示具有常量值的表达式 |
Expression.Add(...) | BinaryExpression | 表示具有(+,-,*,/ )运算的表达式 |
Expression.Property/Field(...) | MemberExpression | 表示访问属性或字段 |
Expression.Call(...) | MethodCallExpression | 表示对静态方法或实例方法的调用 |
Expression.Condition(...) | ConditionalExpression | 表示包含条件运算符的表达式 |
LambdaExpression | 描述一个Lambda表达式 | |
ListInitExpression | 表示包含集合初始值设定项的构造函数调用 | |
NewExpression | 表示构造函数调用 | |
NewArrayExpression | 表示创建新数组并可能初始化改数组的元素 | |
MemberMemberBinding | 表示初始化新创建对象的成员的成员 | |
MemberInitExpression | 表示调用构造函数并初始化新对象的一个或多个成员 | |
MemberAssignment | 表示初始化新创建对象的字段或属性 | |
InvocationExpression | 表示将委托或Lambda表达式应用于参数表达式列表的表达式 | |
TypeBinaryExpression | 表示表达式和类型之间的操作 | |
UnaryExpression | 表示包含一元运算符的表达式 |
3. 拼装Expression
3.1 变量,常量拼装
示例一:常量
static void Test1()
{
// lambda方式
Expression<Func<int>> func = () => 1 + 2;
// 声明常量表达式
ConstantExpression constant1 = Expression.Constant(1, typeof(int));
ConstantExpression constant2 = Expression.Constant(2, typeof(int));
// 声明相加表达式
BinaryExpression add = Expression.Add(constant1, constant2);
// 生成表达式目录树
Expression<Func<int>> exp = Expression.Lambda<Func<int>>(add);
// 表达式目录树生成委托
var ifunc = exp.Compile();
Console.WriteLine(func.Compile().Invoke());
Console.WriteLine(ifunc.Invoke());
}
示例二:常量+变量(2.2示例)
namespace e1
{
using System;
using System.Linq.Expressions;
class Program
{
static void Main(string[] args)
{
// 以此表达式为例,手动拼接,实现相同作用
Expression<Func<int, int, int>> func = (x, y) => x * y + 2;
// 声明变量表达式
ParameterExpression px = Expression.Parameter(typeof(int), "x");
ParameterExpression py = Expression.Parameter(typeof(int), "y");
// 声明常量表达式
ConstantExpression constant = Expression.Constant(2, typeof(int));
// 声明乘积表达式
BinaryExpression multiply = Expression.Multiply(px, py);
// 声明相加表达式
BinaryExpression add = Expression.Add(multiply, constant);
// 声明参数表达式
ParameterExpression[] parameters = new ParameterExpression[] { px, py };
// 生成表达式目录树
Expression<Func<int, int, int>> exp = Expression.Lambda<Func<int, int, int>>(add, parameters);
// 表达式目录树生成委托
var ifunc = exp.Compile();
Console.WriteLine("委托结果:" + func.Compile().Invoke(1, 2));
Console.WriteLine("表达式树:" + ifunc.Invoke(1, 2));
}
}
}
3.2 变量,常量,方法拼接
示例一:特殊类型示例
namespace e1
{
using System;
using System.Linq.Expressions;
using System.Reflection;
class Program
{
static void Main(string[] args)
{
Test2();
}
static void Test2()
{
// lambda方式
Expression<Func<User, bool>> func = (u) => u.uid.ToString().Equals("1");
// 声明变量表达式
ParameterExpression x = Expression.Parameter(typeof(User), "x");
// 获取字段
PropertyInfo property = typeof(User).GetProperty("uid");
// 获取方法
MethodInfo toString = typeof(int).GetMethod("ToString", new Type[] { });
MethodInfo equals = typeof(string).GetMethod("Equals", new Type[] { typeof(string) });
// 设置常量表达式
ConstantExpression constant = Expression.Constant("1");
// 访问字段表达式
MemberExpression propertyExp = Expression.Property(x, property);
// 调用方法表达式
var tostringExp = Expression.Call(propertyExp, toString, new Expression[0]);
var equalsExp = Expression.Call(tostringExp, equals, new Expression[] { constant });
// 生成表达式树
Expression<Func<User, bool>> expression =
Expression.Lambda<Func<User, bool>>(equalsExp, new ParameterExpression[] { x });
User user = new User { uid = 5 };
Console.WriteLine(func.Compile().Invoke(user));
Console.WriteLine(expression.Compile().Invoke(user));
}
}
public class User
{
public int uid { get; set; }
}
}
4. 解析Expression
使用 ExpressionVisitor
解析表达式目录树,ExpressionVisitor
表示表达式树的访问者和重写者
解析流程
- 通过
ExpressionVisitor
这个访问者类 - 调用
Visit
入口(开始)方法解析表达式(自动根据表达式类型执行相应类型的解析方法) -
Lambda
会区分参数和方法体,调度(自动)到更加专业的方法中解析(需要在次调用入口Visit
方法) - 根据表达式的类型,调度(自动)到更加专业的方法中解析(需要在次调用入口
Visit
方法) - 根据旧的模式(表达式)产生一个新的表达式(如果需要重写的话)
- 说明:解析顺序是从右往左解析(如果是二元表达式)
4.1 简单解析
示例一:常量
using System;
using System.Linq.Expressions;
namespace e2
{
class Program
{
static void Main(string[] args)
{
Test1();
}
static void Test1()
{
Expression<Func<int>> expression = () => 1;
CustomVisitor visitor = new CustomVisitor();
var exp = visitor.Modify(expression);
}
}
public class CustomVisitor : ExpressionVisitor
{
public Expression Modify(Expression expression)
{
return this.Visit(expression);
}
protected override Expression VisitConstant(ConstantExpression node)
{
Console.WriteLine("VisitConstant");
return base.VisitConstant(node);
}
}
}
示例二:变量+常量,算术运算(二元运算类型【两个数操作】)
using System;
using System.Linq.Expressions;
namespace e2
{
class Program
{
static void Main(string[] args)
{
Test1();
}
static void Test1()
{
// 1.创建表达式树
Expression<Func<int, int>> expression = (y) => y + 2;
// 2.创建访问类实例(使用继承是为了演示过程)
CustomVisitor visitor = new CustomVisitor();
// 3.调用入口方法,调用入口方法后就会自动进行默认解析(如果没有定义解析过程的话)
var exp = visitor.Modify(expression);
}
}
// 继承访问类,演示过程
public class CustomVisitor : ExpressionVisitor
{
// 调用入口方法,开始解析,自动调用表达式类型对应的解析方法
public Expression Modify(Expression expression)
{
return this.Visit(expression);
}
// 4.调用二元表达式解析方法
protected override Expression VisitBinary(BinaryExpression node)
{
Console.WriteLine("VisitBinary");
// 4.1 判断表达式操作类型
if (node.NodeType == ExpressionType.Add)
{
Expression left = this.Visit(node.Left);
Expression right = this.Visit(node.Right);
return Expression.Subtract(left, right);
}
return base.VisitBinary(node);
}
// 4.调用常量表达式解析方法
protected override Expression VisitConstant(ConstantExpression node)
{
Console.WriteLine("VisitConstant");
return base.VisitConstant(node);
}
}
}
4.2 特殊解析
示例一:属性+常量(二元运算)
using System;
using System.Linq.Expressions;
namespace e2
{
class Program
{
static void Main(string[] args)
{
Test1();
}
static void Test1()
{
Expression<Func<User, bool>> expression = (u) => u.age > 1;
CustomVisitor visitor = new CustomVisitor();
var exp = visitor.Modify(expression);
}
}
public class User
{
public int uid { get; set; }
public int age { get; set; }
}
public class CustomVisitor : ExpressionVisitor
{
public Expression Modify(Expression expression)
{
return this.Visit(expression);
}
// 二元运算类型
protected override Expression VisitBinary(BinaryExpression node)
{
Console.WriteLine("VisitBinary");
if (node.NodeType == ExpressionType.Add)
{
Expression left = this.Visit(node.Left);
Expression right = this.Visit(node.Right);
return Expression.Subtract(left, right);
}
return base.VisitBinary(node);
}
// 属性类型
protected override Expression VisitMember(MemberExpression node)
{
Console.WriteLine("VisitMember");
return base.VisitMember(node);
}
// 常量类型
protected override Expression VisitConstant(ConstantExpression node)
{
Console.WriteLine("VisitConstant");
return base.VisitConstant(node);
}
}
}
示例二:方法(如果要自定义解析处理的话需要预先知道方法名才可)
using System;
using System.Linq.Expressions;
namespace e2
{
class Program
{
static void Main(string[] args)
{
Expression<Func<User, bool>> expression = (u) => u.name.Contains("1");
CustomVisitor visitor = new CustomVisitor();
visitor.Visit(expression);
}
}
public class User
{
public int uid { get; set; }
public int age { get; set; }
public string name { get; set; }
}
public class CustomVisitor : ExpressionVisitor
{
public Expression Modify(Expression expression)
{
return this.Visit(expression);
}
// 方法表达式
protected override Expression VisitMethodCall(MethodCallExpression node)
{
Console.WriteLine("VisitMethodCall:"+ node.Method.Name);
return node;
}
}
}
5. 应用:解析Expression示例
5.1 示例一:简单示例,生成SQL
运算符扩展方法
using System;
using System.Linq.Expressions;
namespace e2
{
internal static class SqlOperator
{
internal static string ToSqlOperator(this ExpressionType type)
{
switch (type)
{
case (ExpressionType.AndAlso):
case (ExpressionType.And):
return "AND";
case (ExpressionType.OrElse):
case (ExpressionType.Or):
return "OR";
case (ExpressionType.Not):
return "NOT";
case (ExpressionType.NotEqual):
return "<>";
case ExpressionType.GreaterThan:
return ">";
case ExpressionType.GreaterThanOrEqual:
return ">=";
case ExpressionType.LessThan:
return "<";
case ExpressionType.LessThanOrEqual:
return "<=";
case (ExpressionType.Equal):
return "=";
default:
throw new Exception("不支持该方法");
}
}
}
}
表达式树解析类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
namespace e2
{
public class ConditionBuilderVisitor : ExpressionVisitor
{
private Stack<string> _StringStack = new Stack<string>();
public string Condition()
{
string condition = string.Concat(this._StringStack.ToArray());
this._StringStack.Clear();
return condition;
}
// 解析 二元表达式 类型
protected override Expression VisitBinary(BinaryExpression node)
{
if (node == null) throw new ArgumentNullException("BinaryExpression");
this._StringStack.Push(")");
base.Visit(node.Right);//解析右边
this._StringStack.Push(" " + node.NodeType.ToSqlOperator() + " ");
base.Visit(node.Left);//解析左边
this._StringStack.Push("(");
return node;
}
// 解析 属性表达式 类型
protected override Expression VisitMember(MemberExpression node)
{
if (node == null) throw new ArgumentNullException("MemberExpression");
this._StringStack.Push(" [" + node.Member.Name + "] ");
return node;
}
// 解析 常量表达式 类型
protected override Expression VisitConstant(ConstantExpression node)
{
if (node == null) throw new ArgumentNullException("ConstantExpression");
this._StringStack.Push(node.Value.ToString());
return node;
}
// 解析 方法表达式 类型
protected override Expression VisitMethodCall(MethodCallExpression m)
{
if (m == null) throw new ArgumentNullException("MethodCallExpression");
string format;
switch (m.Method.Name)
{
case "StartsWith":
format = "({0} LIKE ‘{1}%‘)";
break;
case "Contains":
format = "({0} LIKE ‘%{1}%‘)";
break;
case "EndsWith":
format = "({0} LIKE ‘%{1}‘)";
break;
default:
throw new NotSupportedException(m.NodeType + " is not supported!");
}
this.Visit(m.Object);
this.Visit(m.Arguments[0]);
string right = this._StringStack.Pop();
string left = this._StringStack.Pop();
this._StringStack.Push(String.Format(format, left, right));
return m;
}
}
}
调用执行
using System;
using System.Linq.Expressions;
namespace e2
{
class Program
{
static void Main(string[] args)
{
Test1();
}
static void Test1()
{
Expression<Func<User, bool>> expression = (u) => u.age > 1;
expression = (u) => u.age > 1 && u.uid < 2;
expression = (u) => u.age > 1 && (u.uid < 2 || u.age > 2);
expression = (u) => u.age > 1 && u.name.Contains("李");
ConditionBuilderVisitor visitor = new ConditionBuilderVisitor();
var exp = visitor.Visit(expression);
Console.WriteLine(visitor.Condition());
}
}
public class User
{
public int uid { get; set; }
public int age { get; set; }
}
}
5.2 示例二:表达式树链接,生成SQL
链接表达式扩展方法
using System;
using System.Linq.Expressions;
namespace e2
{
// 建立新表达式
internal class NewExpressionVisitor : ExpressionVisitor
{
// 遍历表达式类型,当遇到参数类型表达式时,替换为我们自己定义的参数
public ParameterExpression _NewParameter { get; private set; }
public NewExpressionVisitor(ParameterExpression param)
{
this._NewParameter = param;
}
public Expression Replace(Expression exp)
{
return this.Visit(exp);
}
// 利用ExpressionVisitor统一参数
protected override Expression VisitParameter(ParameterExpression node)
{
return this._NewParameter;
}
}
/// <summary>
/// 合并表达式 And Or Not扩展
/// </summary>
public static class ExpressionExtend
{
public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
{
ParameterExpression newParameter = Expression.Parameter(typeof(T), "c");
NewExpressionVisitor visitor = new NewExpressionVisitor(newParameter);
var left = visitor.Replace(expr1.Body);// 重新生成了一个表达式目录树
var right = visitor.Replace(expr2.Body);
var body = Expression.And(left, right);
return Expression.Lambda<Func<T, bool>>(body, newParameter);
}
public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
{
// 创建参数表达式
ParameterExpression newParameter = Expression.Parameter(typeof(T), "c");
// 生成一个新的表达式
NewExpressionVisitor visitor = new NewExpressionVisitor(newParameter);
var left = visitor.Replace(expr1.Body);
var right = visitor.Replace(expr2.Body);
var body = Expression.Or(left, right);
return Expression.Lambda<Func<T, bool>>(body, newParameter);
}
public static Expression<Func<T, bool>> Not<T>(this Expression<Func<T, bool>> expr)
{
var candidateExpr = expr.Parameters[0];
var body = Expression.Not(expr.Body);
return Expression.Lambda<Func<T, bool>>(body, candidateExpr);
}
}
}
解析表达式类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
namespace e2
{
public class ConditionBuilderVisitor : ExpressionVisitor
{
private Stack<string> _StringStack = new Stack<string>();
public string Condition()
{
string condition = string.Concat(this._StringStack.ToArray());
this._StringStack.Clear();
return condition;
}
// 解析 二元表达式 类型
protected override Expression VisitBinary(BinaryExpression node)
{
if (node == null) throw new ArgumentNullException("BinaryExpression");
this._StringStack.Push(")");
base.Visit(node.Right);//解析右边
this._StringStack.Push(" " + node.NodeType.ToSqlOperator() + " ");
base.Visit(node.Left);//解析左边
this._StringStack.Push("(");
return node;
}
// 解析 属性表达式 类型
protected override Expression VisitMember(MemberExpression node)
{
if (node == null) throw new ArgumentNullException("MemberExpression");
this._StringStack.Push(" [" + node.Member.Name + "] ");
return node;
}
// 解析 常量表达式 类型
protected override Expression VisitConstant(ConstantExpression node)
{
if (node == null) throw new ArgumentNullException("ConstantExpression");
this._StringStack.Push(node.Value.ToString());
return node;
}
// 解析 方法表达式 类型
protected override Expression VisitMethodCall(MethodCallExpression m)
{
if (m == null) throw new ArgumentNullException("MethodCallExpression");
string format;
switch (m.Method.Name)
{
case "StartsWith":
format = "({0} LIKE ‘{1}%‘)";
break;
case "Contains":
format = "({0} LIKE ‘%{1}%‘)";
break;
case "EndsWith":
format = "({0} LIKE ‘%{1}‘)";
break;
default:
throw new NotSupportedException(m.NodeType + " is not supported!");
}
this.Visit(m.Object);
this.Visit(m.Arguments[0]);
string right = this._StringStack.Pop();
string left = this._StringStack.Pop();
this._StringStack.Push(String.Format(format, left, right));
return m;
}
}
}
调用执行
using System;
using System.Linq.Expressions;
namespace e2
{
class Program
{
static void Main(string[] args)
{
Test1();
}
static void Test1()
{
Expression<Func<User, bool>> expression = (u) => u.age > 1;
expression = expression.And(x=>x.uid>2);
expression = expression.Or(x=>x.uid>2);
expression = expression.Not();
ConditionBuilderVisitor visitor = new ConditionBuilderVisitor();
var exp = visitor.Visit(expression);
Console.WriteLine(visitor.Condition());
}
}
public class User
{
public int uid { get; set; }
public int age { get; set; }
public string name { get; set; }
}
}
5.3 示例三:实体映射
场景;DTO
类转换为 Model
类
方案一:手动,硬编码,不易出错,效率高,但太繁琐
[HttpPost]
public IActionResult Sava(UserDTD dtd)
{
User user = new User
{
uid = dtd.id,
uname = dtd.name
};
_userBll.Sava(user);
}
方案二:使用反射,损耗高,两个类型的属性类型和名称需保证一致
/// <summary>
/// 反射映射
/// </summary>
public class ReflectionMapper
{
/// <summary>
/// 实体转换
/// </summary>
/// <typeparam name="T">传入类型</typeparam>
/// <typeparam name="TResult">返回值类型</typeparam>
/// <param name="tIn">传入参数</param>
/// <returns>转换好的实体</returns>
public static TResult Trans<T, TResult>(T tIn)
{
TResult tOut = Activator.CreateInstance<TResult>();
foreach (var itemOut in tOut.GetType().GetProperties())
{
var propIn = tIn.GetType().GetProperty(itemOut.Name);
itemOut.SetValue(tOut, propIn.GetValue(tIn));
}
foreach (var itemOut in tOut.GetType().GetFields())
{
var fieldIn = tIn.GetType().GetField(itemOut.Name);
itemOut.SetValue(tOut, fieldIn.GetValue(tIn));
}
return tOut;
}
}
方案三:序列化反序列化,损耗高,两个类型的属性类型和名称需保证一致
/// <summary>
/// 使用第三方序列化反序列化工具
/// </summary>
public class SerializeMapper
{
/// <summary>
/// 实体转换
/// </summary>
public static TResult Trans<T, TResult>(T tIn)
{
return JsonConvert.DeserializeObject<TResult>(JsonConvert.SerializeObject(tIn));
}
}
方案四:表达式目录树 + 字典缓存
/// <summary>
/// 生成表达式目录树 字典缓存
/// </summary>
public class ExpressionMapper
{
/// <summary>
/// 字典缓存--hash分布
/// </summary>
private static Dictionary<string, object> _dic = new Dictionary<string, object>();
/// <summary>
/// 实体转换
/// </summary>
public static TResult Trans<T, TResult>(T tIn)
{
string key = string.Format("funckey_{0}_{1}", typeof(T).FullName, typeof(TResult).FullName);
if (!_dic.ContainsKey(key))
{
ParameterExpression parameterExpression = Expression.Parameter(typeof(T), "p");
List<MemberBinding> memberBindingList = new List<MemberBinding>();
foreach (var item in typeof(TResult).GetProperties())
{
MemberExpression property = Expression.Property(parameterExpression, typeof(T).GetProperty(item.Name));
MemberBinding memberBinding = Expression.Bind(item, property);
memberBindingList.Add(memberBinding);
}
foreach (var item in typeof(TResult).GetFields())
{
MemberExpression property = Expression.Field(parameterExpression, typeof(T).GetField(item.Name));
MemberBinding memberBinding = Expression.Bind(item, property);
memberBindingList.Add(memberBinding);
}
MemberInitExpression memberInitExpression = Expression.MemberInit(Expression.New(typeof(TResult)), memberBindingList.ToArray());
Expression<Func<T, TResult>> lambda = Expression.Lambda<Func<T, TResult>>(memberInitExpression, new ParameterExpression[]
{
parameterExpression
});
Func<T, TResult> func = lambda.Compile(); //调用Compile方法将表达式转换成委托
_dic[key] = func; //拼装是一次性的
}
return ((Func<T, TResult>)_dic[key]).Invoke(tIn);
}
}
方案五:表达式目录树 + 泛型缓存(泛型缓存特点:为不同类型的组合去缓存一个结果)
/// <summary>
/// 生成表达式目录树 泛型缓存
/// </summary>
/// <typeparam name="T">传入参数类型</typeparam>
/// <typeparam name="TResult">返回值类型</typeparam>
public class ExpressionGenericMapper<T, TResult>
{
/// <summary>
/// 泛型缓存
/// </summary>
private static Func<T, TResult> _func = null;
/// <summary>
/// 静态构造函数(只会被调用一次)
/// </summary>
static ExpressionGenericMapper()
{
ParameterExpression parameterExpression = Expression.Parameter(typeof(T), "p");
List<MemberBinding> memberBindingList = new List<MemberBinding>();
foreach (var item in typeof(TResult).GetProperties())
{
MemberExpression property = Expression.Property(parameterExpression, typeof(T).GetProperty(item.Name));
MemberBinding memberBinding = Expression.Bind(item, property);
memberBindingList.Add(memberBinding);
}
foreach (var item in typeof(TResult).GetFields())
{
MemberExpression property = Expression.Field(parameterExpression, typeof(T).GetField(item.Name));
MemberBinding memberBinding = Expression.Bind(item, property);
memberBindingList.Add(memberBinding);
}
MemberInitExpression memberInitExpression = Expression.MemberInit(Expression.New(typeof(TResult)), memberBindingList.ToArray());
Expression<Func<T, TResult>> lambda = Expression.Lambda<Func<T, TResult>>(memberInitExpression, new ParameterExpression[]
{
parameterExpression
});
_func = lambda.Compile();//拼装是一次性的
}
/// <summary>
/// 实体转换
/// </summary>
public static TResult Trans(T t)
{
return _func(t);
}
}
6. 扩展补充
6.1 Lambda表达式本质
通过反编译工具得知,Lambda
表达式,其实就是一个方法,在中间语言中,为其分配了一个方法名称(<>
)
6.2 新语法:扩展方法
注意事项
- 实例方法优先于扩展方法(允许存在同名实例方法和扩展方法),注意优先级
- 可以在空引用上调用扩展方法
- 扩展方法必须放在一个非嵌套、非泛型的静态类中,可以被继承
- 至少有一个参数,第一个参数必须附加
this
关键字,不能有任何其他修饰符(out/ref
)
编译结果
public static class Extend
{
public static int ToInt(this int? k)
{
return k ?? 0;
}
}
.class public auto ansi abstract sealed beforefieldinit lq1.Extend
extends [mscorlib]System.Object
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = (
01 00 00 00
)
// Methods
.method public hidebysig static
int32 ToInt (
valuetype [mscorlib]System.Nullable`1<int32> k
) cil managed
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = (
01 00 00 00
)
// Method begins at RVA 0x216c
// Code size 13 (0xd)
.maxstack 1
.locals init (
[0] int32
)
IL_0000: nop
IL_0001: ldarga.s k
IL_0003: call instance !0 valuetype [mscorlib]System.Nullable`1<int32>::GetValueOrDefault()
IL_0008: stloc.0
IL_0009: br.s IL_000b
IL_000b: ldloc.0
IL_000c: ret
} // end of method Extend::ToInt
} // end of class lq1.Extend
6.3 Linq To Object/Sql
linq to object
声明的方法在 Enumerable
类中,针对于 Enumerable
进行处理,数据来之内存数据
操作的表达式是一个委托
inq to sql
声明的方法在 Queryable
类中,针对于 Queryable
进行处理,数据来之内存数据或来自数据库的的数据源
操作的表达式是一个表达式目录树,通过表达式目录树解析成SQL
语句
6.4 yield
迭代器
using System;
using System.Collections.Generic;
namespace lq2
{
class Program
{
static void Main(string[] args)
{
List<User> list = new List<User>
{
new User(){uid=1,uname="a1",age=18,gender=0 },
new User(){uid=2,uname="a2",age=28,gender=1 },
new User(){uid=3,uname="a3",age=23,gender=1 },
new User(){uid=4,uname="a4",age=18,gender=0 },
new User(){uid=5,uname="a5",age=33,gender=1 }
};
var d1 = list.MyWhere(x => x.uid > 3);
foreach (var item in d1)
{
Console.WriteLine(item.uid);
}
}
}
public class User
{
public int uid { get; set; }
public int age { get; set; }
public string uname { get; set; }
public int gender { get; set; }
}
public static class Extend
{
public static IEnumerable<T> MyWhere<T>(this IEnumerable<T> rouse, Func<T, bool> func)
{
foreach (var item in rouse)
{
if (func(item))
{
// yield 迭代器通常与 Enumerable共同使用,实现按需获取(延迟加载)
yield return item;
}
}
}
}
}
6.5 表达式目录树与委托
Expression
一般都是都是配合委托一起来使用的,比如和委托Action
,Func
Expression<Func<T>>
是可以转成Func
的(通过compile()
方法转换),反之则不行
6.6 ORM与表达式树目录的关系
平常项目中经常用到的EF
操作时的扩展方法(Where
之类的)其实传的就是表达式目录树