当前位置: 移动技术网 > IT编程>开发语言>c# > C#表达式树

C#表达式树

2019年12月07日  | 移动技术网IT编程  | 我要评论
前言 在使用 EF 开中我们经常使用 xx.Where(p=>p.Name="张三") 查询数据,之把能这样是因为 EF 框架会把这些C#代码转成Sql语句, 其中主要用到的就是表达式树,今天就来学习一下表达式树。 认识表达式树 Func<int,int,int> func = (a, b) => ...

前言

  在使用 ef 开中我们经常使用 xx.where(p=>p.name="张三") 查询数据,之把能这样是因为 ef 框架会把这些c#代码转成sql语句, 其中主要用到的就是表达式树,今天就来学习一下表达式树。

认识表达式树

func<int,int,int> func = (a, b) => a + b;
expression<func<int,int,int>> expression = (a, b) => a + b;

上面分别是 func 委托和表达式树,看上去很相似,左边只多了 expression<> 右边完全一样,其实还是有很大区别的,对于委托我们只能传递参数来调用,内部的代码在程序运行中是无从得知的,而表达式树在这点上相反,表达式树是一种数据结构,可以通过 c# 代码清晰的获取内部的细节。

表达式树的另一种写法

上面的例子中是使用 lambda 为表达式树赋值,其实还有另一种写法

parameterexpression parametera = expression.parameter(typeof(int),"a");
parameterexpression parameterb = expression.parameter(typeof(int),"b");
binaryexpression binaryexpression = expression.add(parametera, parameterb);
expression<func<int, int, int>> expression = expression.lambda<func<int, int, int>>(binaryexpression,parametera,parameterb);

上面的例子对应于第一种写法,第一种写法是语法糖,其实编译器最终生成还是这种代码,可以通过反编译软件来验证。

 解析表达式树

认识一下表达式树的主要部分

body:表达式主体,例子中是二元表达式,常用的还有

  • constantexpression:常量表达式
  • parameterexpression:参数表达式
  • unaryexpression:一元运算符表达式
  • binaryexpression:二元运算符表达式
  • typebinaryexpression:is运算符表达式
  • conditionalexpression:条件表达式
  • memberexpression:访问字段或属性表达式
  • methodcallexpression:调用成员函数表达式
  • expression<tdelegate>:委托表达式

 

nodetype:节点类型,例子中是 lambda ,常用还有的+,-,*,/,>,=,<,&&,|| 等都有,不过并不是符号而是对应的英文,详情查看 expressiontype 枚举
parameters:表达式的参数,a 和 b

console.writeline(expression.body);
console.writeline(expression.nodetype);
console.writeline(expression.parameters[0]);
console.writeline(expression.parameters[1]);

输出是

(a + b)
lambda
a
b

body 的类型是 expression,例子中的是二元表达式,所以要转换成 binaryexpression 类来查看信息

binaryexpression binaryexpression = (binaryexpression)expression.body;
console.writeline(binaryexpression.left);
console.writeline(binaryexpression.right);
console.writeline(binaryexpression.nodetype);

输出是

a
b
add

 

 刚才是一个简单表达式,再来看两个复杂点的,经过第一次解析后 left 和 right 就是第一种解析的表达式,可以把 left 和 right 再解析一次,最终完全解析,不管多复杂的表达式都可以像这样解析出来

 

 

 

 上面的例子只是为了了解表达式树结构,用这种方法解析存在两个问题

一是 binaryexpression 这里固定了只能解析二元表达式,如果是其它表达式就会报错

二是不知道需要解析多少层才解析完

要解析表达式树要用 c# 里的 expressionvisitor 类,这个类就是专门解析表达式树的,它是一个抽象类,需要建个类继承它,使用过程如下,首先调用父类 visit 方法,在 visit 中会判断表达式的类型是一元(对应visitunary)、二元(对应visitbinary),常量(对应visitconstant)、参数(对应visitparameter)等表达式,然后就会进对应的解析方法中支解析,比如二元表达式的解析方法就是 visitbinary,然后我们重写 visitbinary 

下面是使用 expressionvisitor 解析表达式树的例子,这么说并不完全对,解析代码是 expressionvisitor 已经写好的,我们做的只解析过程中加入一些自己的代码而已

 

 

 

 首尾呼应

最后来实现一个简单的由表达式树生成sql语句的功能

class myvisitor : expressionvisitor
    {
        private string tablename;
        private stringbuilder sbsql = new stringbuilder();
        public override expression visit(expression node)
        {
            return base.visit(node);
        }
        protected override expression visitbinary(binaryexpression node)
        {
            base.visit(node.left);
            sbsql.append(expressiontypetosql(node.nodetype));
            base.visit(node.right);
            return node;
        }
        public string getsqlstring()
        {
            return "select * from "+tablename+" where "+sbsql.tostring();
        }
        protected override expression visitconstant(constantexpression node)
        {
            if (node.type == typeof(int))
            {
                sbsql.append( node.value);
            }
            else
            {
                sbsql.append("'"+node.value+"'");
            }
            return base.visitconstant(node);
        }
        protected override expression visitparameter(parameterexpression node)
        {
            if (tablename == null)
            {
                tablename = "[" + node.type.name + "]";
            }
            return base.visitparameter(node);
        }
        protected override expression visitmember(memberexpression node)
        {
            sbsql.append("[" + node.member.name + "]");
            return base.visitmember(node);
        }
        public string expressiontypetosql(expressiontype expressiontype)
        {
            switch (expressiontype)
            {
                case expressiontype.add:
                    return " + ";
                case expressiontype.and:
                case expressiontype.andalso:
                    return " and ";
                case expressiontype.equal:
                    return " = ";
                case expressiontype.notequal:
                    return " != ";
                case expressiontype.greaterthan:
                    return " > ";
                case expressiontype.greaterthanorequal:
                    return " >= ";
                case expressiontype.lessthan:
                    return " < ";
                case expressiontype.lessthanorequal:
                    return " <= ";
                case expressiontype.multiply:
                    return " * ";
                case expressiontype.or:
                case expressiontype.orelse:
                    return " or ";
                default:
                    return "";
            }
        }
    }
expression<func<person, bool>> expression = p=>p.name=="张三"&&p.name!="李四";
myvisitor myvisitor = new myvisitor();
myvisitor.visit(expression);
console.writeline(myvisitor.getsqlstring());

 

写得有点乱,忘见谅

如您对本文有疑问或者有任何想说的,请 点击进行留言回复,万千网友为您解惑!

相关文章:

验证码:
移动技术网