干货!表达式树解析"框架"(1)
最新设计请移步 轻量级表达式树解析框架Faller http://www.cnblogs.com/blqw/p/Faller.html
关于我和表达式树
其实我也没有深入了解表达式树一些内在实现的原理,所以具体来说它到底是个什么东西我也不是很清楚,我的理解只是他是可以将'部分'委托构造成对象,方便我们对他进行解析; 虽然我没有完全搞懂表达式树,但这并不妨碍我使用它(如果以后有时间,我想我会更深入的去和它接触一下)
Lamda + 表达式树的性能瓶颈
对于Lamda表达式树我的感觉是又爱又恨
书写和阅读方面,无疑是非常成功的,他大大的减少了书写难度,增加了可读性
但另一方面,在程序的性能上又是如此的糟糕
来看下面一个栗子:
static void Main()
{
Where1(u => u.Name == "");
Where2(u => u.Name == "");
} public static void Where1(Expression<Func<User, bool>> expr)
{ } public static void Where2(Func<User, bool> expr)
{ }
栗子中的 Where1 和 Where2 两个方法,唯一的不同,一个是委托,一个是表达式树
同样运行Where1和Where2 一万次,Where2是0ms,Where1是57ms
也就是说从Func<User, bool>转为Expression<Func<User, bool>>一万次需要57ms
这对于我这样一个追求性能的人来说,实在是有点难受!
到不至于不能接受,只有有点难受
但从另一方面我也在考虑,为什么像这样的lamda不能直接预编译成Expression呢?期待微软的优化吧~
伪框架
为什么我的标题中的框架为带有引号?
因为我觉得这其实是一个伪框架
但他确实能帮助我们更好的解析Expression对象,或许应该把他称之为解决方案或是别的
不过~管他呢!能用就行了
你应该了解的Expression
刚才说了虽然我也没有完全搞懂,但是有些东西还是应该知道的
比如:
- 以Expression作为基类的子类一共有多少个
- 他们分别是干什么的
第一个问题比较简单
- 现在在vs中敲下 System.Linq.Expressions.Expression
- 然后按F1
- 如果顺利的话,你现在已经打开了"MSDN"
- 如果没有的话就手动点一下吧
- 然后滚动到屏幕最下面
好了这里看到的就是所有`public`的子类(其实没有公开的还有更多)
至于他们分别是干什么用的,每一个都有自己不同的用法,这里不可能一一说明了,下面的内容也只会涉及到一部分,其他就要自己慢慢研究了
举个栗子:
BinaryExpression ----表示包含二元运算符的表达式
最基础的用法就是它的三个属性Left ,Right ,NodeType
Left 获取二元运算的左操作数。
Right 获取二元运算的右操作数。
NodeType 获取此 Expression 的节点类型。
如 it = it.Name == "blqw"就是一个BinaryExpression
Left = it.Name
Right = "blqw"
NodeType = Equals
大概就是这么一个意思吧
效果预览
框架结构
嗯.允许我还是叫他框架吧,毕竟听上去比较高端大气上档次啊
暂时是这样
- Parsers文件夹里面的是具体对应每一种表达式树的解析的具体实现
- ExpressionParser 表达式树解析器抽象基类,实现IExpressionParser
- ExpressionTypeCode 枚举,枚举了所有的类型的表达式树
- IExpressionParser 表达式树解析器接口
- Parser 调用解析器的静态对象,也可以看作入口或工厂,根据表达式树类型调用具体类
- ParserArgs 解析器参数,在解析表达式树的方法中保持传递,可以保存解析中所使用的参数和保存解析结果
代码
public class ParserArgs
{
public ParserArgs()
{
Builder = new StringBuilder();
} public StringBuilder Builder { get; private set; } public IList<ParameterExpression> Forms { get; set; } public readonly string[] FormsAlias = { "it", "A", "B", "C", "D", "E" };
}
ParserArgs
IExpressionParser
/// <summary> 表达式树解析接口
/// </summary>
public interface IExpressionParser
{
void Select(Expression expr, ParserArgs args);
void Where(Expression expr, ParserArgs args);
void GroupBy(Expression expr, ParserArgs args);
void Having(Expression expr, ParserArgs args);
void OrderBy(Expression expr, ParserArgs args);
void Object(Expression expr, ParserArgs args);
}
/// <summary> 表达式树解析抽象泛型类
/// </summary>
public abstract class ExpressionParser<T> : IExpressionParser
where T : Expression
{
public abstract void Select(T expr, ParserArgs args);
public abstract void Where(T expr, ParserArgs args);
public abstract void GroupBy(T expr, ParserArgs args);
public abstract void Having(T expr, ParserArgs args);
public abstract void OrderBy(T expr, ParserArgs args);
public abstract void Object(T expr, ParserArgs args); public void Select(Expression expr, ParserArgs args)
{
Select((T)expr, args);
} public void Where(Expression expr, ParserArgs args)
{
Where((T)expr, args);
} public void GroupBy(Expression expr, ParserArgs args)
{
GroupBy((T)expr, args);
} public void Having(Expression expr, ParserArgs args)
{
Having((T)expr, args);
} public void OrderBy(Expression expr, ParserArgs args)
{
OrderBy((T)expr, args);
} public void Object(Expression expr, ParserArgs args)
{
Object((T)expr, args);
}
}
ExpressionParser
/// <summary> 表达式类型枚举
/// </summary>
public enum ExpressionTypeCode
{
/// <summary> 未知类型表达式
/// </summary>
Unknown = ,
/// <summary> 空表达式 null
/// </summary>
Null = ,
/// <summary> 表示包含二元运算符的表达式。
/// </summary>
BinaryExpression = ,
/// <summary> 表示一个包含可在其中定义变量的表达式序列的块。
/// </summary>
BlockExpression = ,
/// <summary> 表示包含条件运算符的表达式。
/// </summary>
ConditionalExpression = ,
/// <summary> 表示具有常量值的表达式。
/// </summary>
ConstantExpression = ,
/// <summary> 发出或清除调试信息的序列点。 这允许调试器在调试时突出显示正确的源代码。
/// </summary>
DebugInfoExpression = ,
/// <summary> 表示类型或空表达式的默认值。
/// </summary>
DefaultExpression = ,
/// <summary> 表示动态操作。
/// </summary>
DynamicExpression = ,
/// <summary> 表示无条件跳转。 这包括 return 语句、break 和 continue 语句以及其他跳转。
/// </summary>
GotoExpression = ,
/// <summary> 表示编制属性或数组的索引。
/// </summary>
IndexExpression = ,
/// <summary> 表示将委托或 lambda 表达式应用于参数表达式列表的表达式。
/// </summary>
InvocationExpression = ,
/// <summary> 表示一个标签,可以将该标签放置在任何 Expression 上下文中。
/// </summary>
LabelExpression = ,
/// <summary> 描述一个 lambda 表达式。 这将捕获与 .NET 方法体类似的代码块。
/// </summary>
LambdaExpression = ,
/// <summary> 表示包含集合初始值设定项的构造函数调用。
/// </summary>
ListInitExpression = ,
/// <summary> 表示无限循环。 可以使用“break”退出它。
/// </summary>
LoopExpression = ,
/// <summary> 表示访问字段或属性。
/// </summary>
MemberExpression = ,
/// <summary> 表示调用构造函数并初始化新对象的一个或多个成员。
/// </summary>
MemberInitExpression = ,
/// <summary> 表示对静态方法或实例方法的调用。
/// </summary>
MethodCallExpression = ,
/// <summary> 表示创建新数组并可能初始化该新数组的元素。
/// </summary>
NewArrayExpression = ,
/// <summary> 表示构造函数调用。
/// </summary>
NewExpression = ,
/// <summary> 表示命名的参数表达式。
/// </summary>
ParameterExpression = ,
/// <summary> 一个为变量提供运行时读/写权限的表达式。
/// </summary>
RuntimeVariablesExpression = ,
/// <summary> 表示一个控制表达式,该表达式通过将控制传递到 SwitchCase 来处理多重选择。
/// </summary>
SwitchExpression = ,
/// <summary> 表示 try/catch/finally/fault 块。
/// </summary>
TryExpression = ,
/// <summary> 表示表达式和类型之间的操作。
/// </summary>
TypeBinaryExpression = ,
/// <summary> 表示包含一元运算符的表达式。
/// </summary>
UnaryExpression = ,
}
ExpressionTypeCode
/// <summary> 解析器静态对象
/// </summary>
public static class Parser
{
private static readonly IExpressionParser[] Parsers = InitParsers(); static IExpressionParser[] InitParsers()
{
var codes = Enum.GetValues(typeof(ExpressionTypeCode));
var parsers = new IExpressionParser[codes.Length]; foreach (ExpressionTypeCode code in codes)
{
if (code.ToString().EndsWith("Expression"))
{
var type = Type.GetType(typeof(Parser).Namespace + "." + code.ToString() + "Parser");
if (type != null)
{
parsers[(int)code] = (IExpressionParser)Activator.CreateInstance(type);
}
}
}
return parsers;
} /// <summary> 得到表达式类型的枚举对象 </summary>
/// <param name="expr"> 扩展对象:Expression </param>
/// <returns> </returns>
public static ExpressionTypeCode GetCodeType(Expression expr)
{
if (expr == null)
{
return ExpressionTypeCode.Null;
}
if (expr is BinaryExpression)
{
return ExpressionTypeCode.BinaryExpression;
}
if (expr is BlockExpression)
{
return ExpressionTypeCode.BlockExpression;
}
if (expr is ConditionalExpression)
{
return ExpressionTypeCode.ConditionalExpression;
}
if (expr is ConstantExpression)
{
return ExpressionTypeCode.ConstantExpression;
}
if (expr is DebugInfoExpression)
{
return ExpressionTypeCode.DebugInfoExpression;
}
if (expr is DefaultExpression)
{
return ExpressionTypeCode.DefaultExpression;
}
if (expr is DynamicExpression)
{
return ExpressionTypeCode.DynamicExpression;
}
if (expr is GotoExpression)
{
return ExpressionTypeCode.GotoExpression;
}
if (expr is IndexExpression)
{
return ExpressionTypeCode.IndexExpression;
}
if (expr is InvocationExpression)
{
return ExpressionTypeCode.InvocationExpression;
}
if (expr is LabelExpression)
{
return ExpressionTypeCode.LabelExpression;
}
if (expr is LambdaExpression)
{
return ExpressionTypeCode.LambdaExpression;
}
if (expr is ListInitExpression)
{
return ExpressionTypeCode.ListInitExpression;
}
if (expr is LoopExpression)
{
return ExpressionTypeCode.LoopExpression;
}
if (expr is MemberExpression)
{
return ExpressionTypeCode.MemberExpression;
}
if (expr is MemberInitExpression)
{
return ExpressionTypeCode.MemberInitExpression;
}
if (expr is MethodCallExpression)
{
return ExpressionTypeCode.MethodCallExpression;
}
if (expr is NewArrayExpression)
{
return ExpressionTypeCode.NewArrayExpression;
}
if (expr is NewExpression)
{
return ExpressionTypeCode.NewArrayExpression;
}
if (expr is ParameterExpression)
{
return ExpressionTypeCode.ParameterExpression;
}
if (expr is RuntimeVariablesExpression)
{
return ExpressionTypeCode.RuntimeVariablesExpression;
}
if (expr is SwitchExpression)
{
return ExpressionTypeCode.SwitchExpression;
}
if (expr is TryExpression)
{
return ExpressionTypeCode.TryExpression;
}
if (expr is TypeBinaryExpression)
{
return ExpressionTypeCode.TypeBinaryExpression;
}
if (expr is UnaryExpression)
{
return ExpressionTypeCode.UnaryExpression;
}
return ExpressionTypeCode.Unknown;
} /// <summary> 得到当前表达式对象的解析组件 </summary>
/// <param name="expr"> 扩展对象:Expression </param>
/// <returns> </returns>
public static IExpressionParser GetParser(Expression expr)
{
var codetype = GetCodeType(expr);
var parser = Parsers[(int)codetype];
if (parser == null)
{
switch (codetype)
{
case ExpressionTypeCode.Unknown:
throw new ArgumentException("未知的表达式类型", "expr");
case ExpressionTypeCode.Null:
throw new ArgumentNullException("expr", "表达式为空");
default:
throw new NotImplementedException("尚未实现" + codetype + "的解析");
}
}
return parser;
} public static void Select(Expression expr, ParserArgs args)
{
GetParser(expr).Select(expr, args);
} public static void Where(Expression expr, ParserArgs args)
{
GetParser(expr).Where(expr, args);
} public static void GroupBy(Expression expr, ParserArgs args)
{
GetParser(expr).GroupBy(expr, args);
} public static void Having(Expression expr, ParserArgs args)
{
GetParser(expr).Having(expr, args);
} public static void OrderBy(Expression expr, ParserArgs args)
{
GetParser(expr).OrderBy(expr, args);
} public static void Object(Expression expr, ParserArgs args)
{
GetParser(expr).Object(expr, args);
} }
Parser
原理分解
首先将所有类型的表达式树以枚举的形式表现出来,1来是为了更直观便于2是为了给他们编号
有了编号就可以方便的在数组或集合中给他们安排位置了
初始化
在Parser类中,放置一个静态字段
private static readonly IExpressionParser[] Parsers = InitParsers();
在InitParsers方法中,使用反射查找当前命名空间下名称为 枚举名 + Parser 的类,如果有则实例化,并根据枚举的值,在集合中保存
ps:枚举名 + Parser 作为解析器的命名规则,仅仅是为了方便反射调用,Parsers[0] = new xxx() 这个依然是可以由后期调用绑定的
调用
然后提供一个方法,用于获取当前表达式对象对应的枚举值
public static ExpressionTypeCode GetCodeType(Expression expr)
{
if (expr == null)
{
return ExpressionTypeCode.Null;
}
if (expr is BinaryExpression)
{
return ExpressionTypeCode.BinaryExpression;
}
if (expr is BlockExpression)
{
return ExpressionTypeCode.BlockExpression;
}
...
...
return ExpressionTypeCode.Unknown;
}
这里的方法我没有选择用反射来获取枚举值,还是基于对性能的考虑,这样测试快5~10倍,有兴趣的可以测试一下
public static ExpressionTypeCode GetCodeType(Expression expr)
{
if (expr == null)
{
return ExpressionTypeCode.Null;
}
ExpressionTypeCode tc;
if (Enum.TryParse(expr.GetType().Name, out tc))
{
return tc;
}
return ExpressionTypeCode.Unknown;
}
反射代码
得到枚举之后,就可以按枚举的值,从集合中获取已经实例化的解析器为了方便调用,写了一个方法GetParser
public static IExpressionParser GetParser(Expression expr)
{
var codetype = GetCodeType(expr);
var parser = Parsers[(int)codetype];
if (parser == null)
{
switch (codetype)
{
case ExpressionTypeCode.Unknown:
throw new ArgumentException("未知的表达式类型", "expr");
case ExpressionTypeCode.Null:
throw new ArgumentNullException("expr", "表达式为空");
default:
throw new NotImplementedException("尚未实现" + codetype + "的解析");
}
}
return parser;
}
得到解析器之后,就可以做爱做的事了,
好了我也有点累了,先这样吧
未完待续...
干货!表达式树解析"框架"(1)的更多相关文章
- 干货!表达式树解析"框架"(2)
最新设计请移步 轻量级表达式树解析框架Faller http://www.cnblogs.com/blqw/p/Faller.html 为了过个好年,我还是赶快把这篇完成了吧 声明 本文内容需要有一定 ...
- 干货!表达式树解析"框架"(3)
最新设计请移步 轻量级表达式树解析框架Faller http://www.cnblogs.com/blqw/p/Faller.html 这应该是年前最后一篇了,接下来的时间就要陪陪老婆孩子了 关于表达 ...
- 轻量级表达式树解析框架Faller
有话说 之前我写了3篇关于表达式树解析的文章 干货!表达式树解析"框架"(1) 干货!表达式树解析"框架"(2) 干货!表达式树解析"框架" ...
- 表达式树解析"框架"
干货!表达式树解析"框架"(2) 为了过个好年,我还是赶快把这篇完成了吧 声明 本文内容需要有一定基础的开发人员才可轻松阅读,如果有难以理解的地方可以跟帖询问,但我也不一定能回 ...
- Lambda表达式树解析(下)
概述 前面章节,总结了Lambda树的构建,那么怎么解析Lambda表达式树那?Lambda表达式是一种委托构造而成,如果能够清晰的解析Lambda表达式树,那么就能够理解Lambda表达式要传递的正 ...
- Lambda表达式树解析(下)包含自定义的provider和查询
概述 前面章节,总结了Lambda树的构建,那么怎么解析Lambda表达式树那?Lambda表达式是一种委托构造而成,如果能够清晰的解析Lambda表达式树,那么就能够理解Lambda表达式要传递的正 ...
- 介绍一个可以将Expression表达式树解析成Transact-SQL的项目Expression2Sql
一.Expression2Sql介绍 Expression2Sql是一个可以将Expression表达式树解析成Transact-SQL的项目.简单易用,几分钟即可上手使用,因为博主在设计Expres ...
- C#3.0新增功能10 表达式树 03 支持表达式树的框架类型
连载目录 [已更新最新开发文章,点击查看详细] 存在可与表达式树配合使用的 .NET Core framework 中的类的大型列表. 可以在 System.Linq.Expressions 查 ...
- 借助表达式树感受不一样的CRUD
借助表达式树感受不一样的CRUD Intro 最近有个想法,想不写 sql 语句,做一个类似于 ORM 的东西,自己解析表达式树,生成要执行的 sql 语句,最后再执行 sql 语句,返回相应结果. ...
随机推荐
- 解决 U 盘写保护的问题
插了一个 U 盘,想格式化或者创建文件,但是提示我 U 盘是写保护的. 解决方法如下: Open a command prompt type DISKPART press type LIST VOLU ...
- Spring MVC类型转换
类型转换器引入 为什么页面上输入"12",可以赋值给Handler方法对应的参数?这是因为框架内部帮我们做了类型转换的工作.将String转换成int 但默认类型转换器并不是可以将 ...
- supervisor-3:xml_rpc
别人博客转载,做个记录 原文链接:http://lixcto.blog.51cto.com/4834175/1540795 supervisor提供的两种管理方式,supervisorctl和web其 ...
- laravel 操作 redis
laravel框架中本身已经存在相应的redis的配置我们在使用的时候只需要更改配置即可,但是在使用的时候一定要注意命名空间的问题,具体可查看config/app.php下面的aliases数组中具体 ...
- Python 学习手册, char 14 - 15
Char 14 迭代器和解析器 可迭代的 : 支持iter的一个对象 迭代器 : iter 所返回的一个支持next(I)的对象 Python迭代工具会自动调用这些函数,我们也可以手动地应用迭代协议 ...
- Ubuntu14.04 64位机上安装cuda8.0 cudnn5.0操作步骤 - 网络资源是无限的
查看Ubuntu14.04 64位上显卡信息,执行: lspci | grep -i vga lspci -v -s 01:00.0 nvidia-smi 第一条此命令可以显示一些显卡的相关信息:如果 ...
- 【统计学习】主成分分析PCA(Princple Component Analysis)从原理到实现
[引言]--PCA降维的作用 面对海量的.多维(可能有成百上千维)的数据,我们应该如何高效去除某些维度间相关的信息,保留对我们"有用"的信息,这是个问题. PCA给出了我们一种解决 ...
- linux菜鸟日记(5)
iptables详细语法及配置: SNAT:源地址转换DNAT:目标地址转换PNAT:端口地址转换 ----------------------------------iptables规则链 路由以后 ...
- 【BZOJ1623】 [Usaco2008 Open]Cow Cars 奶牛飞车 贪心
SB贪心,一开始还想着用二分,看了眼黄学长的blog,发现自己SB了... 最小道路=已选取的奶牛/道路总数. #include <iostream> #include <cstdi ...
- 【ORACLE】 表空间信息
Linux 查看磁盘空间命令 格式: df -hl 显示格式为: 文件系统 容量 已用 可用 已用% 挂载点 [root@localhost opt]# dfFilesystem 1K-blo ...