joe 发表于 2022-7-22 17:04:56

C# 表达式目录树Expression的实现

目录
[*]
[*]
[*]

[*]linq to sql
[*]expressionvisitor
[*]
[*]


表达式目录树表达式目录树:语法树,或者说是一种数据结构
1.表达式目录树expression:system.linq.expressions;
2.描述了多个变量或者和常量之间的关系,按照一定的规则进行组装!
[*]可以向委托一样使用lambd表达式快捷声明;
[*]不能有语句体,声明只能有一行代码;
[*]可以通过compile(),编译成一个委托;

1
2
3
4
5
6
7
8
9
10
11
func<int, int, int> func = (m, n) =>
{
    int i = 0;
    return m * n + 2;
};//委托拉姆达表达式其实是作为委托的一个参数,本质是一个方法(匿名方法)
expression<func<int, int, int>> exp = (m, n) => m * n + 2; //数据结构--就像对一个计算做了一个精确的描述,展开之后发现,分为左边,右边,每个元素都可以把值都获取出来,二叉树
var erpplu= exp.compile();//表达式目录树可以通过compile 转换成一个委托

//表达式目录树:语法树,或者说是一种数据结构
int iresult1 = func.invoke(12, 23);
int iresult2 = exp.compile().invoke(12, 23);





http://images5.10qianwan.com/10qianwan/20210926/b_0_202109261910108267.pnghttp://images5.10qianwan.com/10qianwan/20210926/b_0_202109261910102968.jpg
表达式目录树的拼装手动拼装表达式目录树,不是用的lambda的快捷方式
1
2
3
4
5
6
7
8
9
//表达式目录树的拼装
expression<func<int>> expression = () => 123 + 234;//两个常量相加-----表达式目录树的快捷声明

expression constant123 = expression.constant(123);
expression constant234 = expression.constant(234);
expression expressionadd = expression.add(constant123, constant234);
var exp = expression.lambda<func<int>>(expressionadd);
var func = exp.compile();
int iresult = func.invoke();






1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
expression<func<int, int, int>> exp = (m, n) => m * n + m + n + 2; //快捷声明--其实编译器提供的便捷功能---语法糖--具体实现可通过反编译工具查看

//具体实现可通过反编译工具查看
parameterexpression parameterexpression = expression.parameter(typeof(int), "m");
parameterexpression parameterexpression2 = expression.parameter(typeof(int), "n");
expression expcontant2 = expression.constant(2, typeof(int));

expression multipley = expression.multiply(parameterexpression, parameterexpression2);
expression expadd = expression.add(multipley, parameterexpression);

expression expadd1 = expression.add(expadd, parameterexpression2);
expression expadd2 = expression.add(expadd1, expcontant2);
expression<func<int, int, int>> expression = expression.lambda<func<int, int, int>>(expadd2, new parameterexpression[]
{
parameterexpression,
parameterexpression2
});
func<int, int, int> fun = expression.compile();
int iresult = fun.invoke(10, 11);






1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
var peoplequery = new list<people>().asqueryable();
expression<func<people, bool>> lambda = x => x.id.tostring().equals("5");
peoplequery.where(lambda);

parameterexpression parameterexpression = expression.parameter(typeof(people), "x");
fieldinfo idfield = typeof(people).getfield("id");
var idexp = expression.field(parameterexpression, idfield);

methodinfo tostring = typeof(int).getmethod("tostring", new type);
var tostringexp = expression.call(idexp, tostring, array.empty<expression>());
var equals = typeof(string).getmethod("equals", new type[] { typeof(string) });
expression expressionconstant5 = expression.constant("5", typeof(string));
var equalsexp = expression.call(tostringexp, equals, new expression[]
{
    expressionconstant5
});
expression<func<people, bool>> expression = expression.lambda<func<people, bool>>(equalsexp, new parameterexpression[]
{
parameterexpression
});
func<people, bool> func = expression.compile();
var bresult = func.invoke(new people()
{
    id = 5,
    name = "海贝"
});

new list<people>().asqueryable().where(expression);






应用
linq to sql
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var dbset = new list<people>().asqueryable();//ef dbset
dbset.where(p => p.age == 25 & p.name.contains("阳光下的微笑"));

expression<func<people, bool>> exp = null;
console.writeline("用户输入个名称,为空就跳过");
string name = console.readline();
if (!string.isnullorwhitespace(name))
{
    exp = p => p.name.contains(name);
}
console.writeline("用户输入个最小年纪,为空就跳过");
string age = console.readline();
if (!string.isnullorwhitespace(age) && int.tryparse(age, out int iage))
{
    exp = p => p.age > iage;
}





上面的玩法是不是只有最后一个条件才生效?如果需要多个条件都满足;怎么办? 当然是拼装啊;
拼装可以从最小粒度来组装表达式目录树;如果有一个封装,你把各种条件给我,我从最小粒度开始一个一个的拼装起来,不就是一个长的表达式目录树了吗?解决方案:
调用方可以组装一个很长的表达式目录树传递过来;
表达式目录树传递过来以后,在这里应该做什么?应该解析;
所有信息都在表达式目录树里面,自然也可以把他解析(找出来)
解析就可以通过expressionvisitor解析----生成对应的sql语句;
expressionvisitor表达式目录树的访问者----访问者模式;
1.visit方法–访问表达式目录树的入口—分辨是什么类型的表达式目录
2.调度到更加专业的方法中进一步访问,访问一遍之后,生成一个新的表达式目录 —有点像递归,不全是递归;
3.因为表达式目录树是个二叉树,expressionvisitor一直往下访问,一直到叶节点;那就访问了所有的节点;
4.在访问的任何一个环节,都可以拿到对应当前环节的内容(参数名称、参数值。。),就可以进一步扩展;为什么要使用表达式目录树来拼装解析呢:
1.可以提高重用性;
2.如果封装好一个方法,接受一个表达式目录树,在解析的时候,其实就是不断的访问,访问有规则;
3.任何一个表达式目录树都可以调用当前方法来解析;
4.表达式目录树可以支持泛型;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
{
    expression<func<people, bool>> lambda = x => x.age > 5 && x.id > 5
                                             && x.name.startswith("1") //like '1%'
                                             && x.name.endswith("1") //like '%1'
                                             && x.name.contains("1");//like '%1%'

    //string sql = string.format("delete from [{0}] where >5 and >5"
      , typeof(people).name
      , " >5 and >5" );

    conditionbuildervisitor vistor = new conditionbuildervisitor();
    vistor.visit(lambda);
    console.writeline(vistor.condition());
}
{
   // ((( > '5') and( = )) or( > '5' ))
   string name = "aaa";
    expression<func<people, bool>> lambda = x => x.age > 5 && x.name == name || x.id > 5;
    conditionbuildervisitor vistor = new conditionbuildervisitor();
    vistor.visit(lambda);
    console.writeline(vistor.condition());
}
{
    expression<func<people, bool>> lambda = x => x.age > 5 || (x.name == "a" && x.id > 5);
    conditionbuildervisitor vistor = new conditionbuildervisitor();
    vistor.visit(lambda);
    console.writeline(vistor.condition());
}
{
    expression<func<people, bool>> lambda = x => (x.age > 5 || x.name == "a") && x.id > 5;
    conditionbuildervisitor vistor = new conditionbuildervisitor();
    vistor.visit(lambda);
    console.writeline(vistor.condition());
}





自己封装的解析器,这就是ef6的底层原理,根据表达式树自动生成相应的sql语句。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
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;
    }

    /// <summary>
    /// 如果是二元表达式
    /// </summary>
    /// <param name="node"></param>
    /// <returns></returns>
    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;
    }

    /// <summary>
    /// 解析属性
    /// </summary>
    /// <param name="node"></param>
    /// <returns></returns>
    protected override expression visitmember(memberexpression node)
    {
      if (node == null) throw new argumentnullexception("memberexpression");
      //this._stringstack.push(" [" + node.member.name + "] ");
      return node;
      if (node.expression is constantexpression)
      {
            var value1 = this.invokevalue(node);
            var value2 = this.reflectionvalue(node);
            //this.conditionstack.push($"'{value1}'");
            this._stringstack.push("'" + value2 + "'");
      }
      else
      {
            this._stringstack.push(" [" + node.member.name + "] ");
      }
      return node;
    }


    private object invokevalue(memberexpression member)
    {
      var objexp = expression.convert(member, typeof(object));//struct需要
      return expression.lambda<func<object>>(objexp).compile().invoke();
    }

    private object reflectionvalue(memberexpression member)
    {
      var obj = (member.expression as constantexpression).value;
      return (member.member as fieldinfo).getvalue(obj);
    }

    /// <summary>
    /// 常量表达式
    /// </summary>
    /// <param name="node"></param>
    /// <returns></returns>
    protected override expression visitconstant(constantexpression node)
    {
      if (node == null) throw new argumentnullexception("constantexpression");
      this._stringstack.push(" '" + node.value + "' ");
      return node;
    }
    /// <summary>
    /// 方法表达式
    /// </summary>
    /// <param name="m"></param>
    /// <returns></returns>
    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);
      string right = this._stringstack.pop();
      string left = this._stringstack.pop();
      this._stringstack.push(string.format(format, left, right));

      return m;
    }
}






1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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("不支持该方法");
      }

    }
}






表达式目录扩展表达式目录树动态拼接的实现方式:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
/// <summary>
/// 合并表达式 and ornot扩展
/// </summary>
public static class expressionextend
{
    /// <summary>
    /// 合并表达式 expr1 and expr2
    /// </summary>
    /// <typeparam name="t"></typeparam>
    /// <param name="expr1"></param>
    /// <param name="expr2"></param>
    /// <returns></returns>
    public static expression<func<t, bool>> and<t>(this expression<func<t, bool>> expr1, expression<func<t, bool>> expr2)
    {
      //return expression.lambda<func<t, bool>>(expression.andalso(expr1.body, expr2.body), expr1.parameters); 错误的写法,两个表达式不是同一个参数
      //将两个表达式的参数统一为参数c
      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);

    }
    /// <summary>
    /// 合并表达式 expr1 or expr2
    /// </summary>
    /// <typeparam name="t"></typeparam>
    /// <param name="expr1"></param>
    /// <param name="expr2"></param>
    /// <returns></returns>
    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;
      var body = expression.not(expr.body);

      return expression.lambda<func<t, bool>>(body, candidateexpr);
    }
}






1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/// <summary>
/// 建立新表达式
/// </summary>
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);
    }
    protected override expression visitparameter(parameterexpression node)
    {
      return this._newparameter;
    }
}





调用方如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
    {
      expression<func<people, bool>> lambda1 = x => x.age > 5;
      expression<func<people, bool>> lambda2 = x => x.id > 5;

      //expression<func<people, bool>> newexpress = x => x.age > 5 && x.id > 5;

      expression<func<people, bool>> lambda3 = lambda1.and(lambda2); //且
      expression<func<people, bool>> lambda4 = lambda1.or(lambda2);//或
      expression<func<people, bool>> lambda5 = lambda1.not();//非
      do1(lambda3);
      do1(lambda4);
      do1(lambda5);
    }

private static void do1(expression<func<people, bool>> func)
{
    list<people> people = new list<people>()
    {
      new people(){id=4,name="123",age=4},
      new people(){id=5,name="234",age=5},
      new people(){id=6,name="345",age=6},
    };

    list<people> peoplelist = people.where(func.compile()).tolist();
}





对象深拷贝硬编码
1
2
3
4
5
6
peoplecopy peoplecopy = new peoplecopy()
{
    id = people.id,
    name = people.name,
    age = people.age
};





通过反射实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class reflectionmapper
{
    /// <summary>
    /// 反射
    /// </summary>
    /// <typeparam name="tin"></typeparam>
    /// <typeparam name="tout"></typeparam>
    /// <param name="tin"></param>
    /// <returns></returns>
    public static tout trans<tin, tout>(tin tin)
    {
      tout tout = activator.createinstance<tout>();
      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;
    }
}





通过序列化实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/// <summary>
/// 使用第三方序列化反序列化工具
/// 还有automapper
/// </summary>
public class serializemapper
{
    /// <summary>
    /// 序列化反序列化方式
    /// </summary>
    /// <typeparam name="tin"></typeparam>
    /// <typeparam name="tout"></typeparam>
    public static tout trans<tin, tout>(tin tin)
    {
      return jsonconvert.deserializeobject<tout>(jsonconvert.serializeobject(tin));
    }
}





反射和序列化两种实现方式性能不太好;
通过表达式目录树实现通过表达式目录树动态的生成硬编码
1
2
3
4
5
6
7
func<people, peoplecopy> func = p => new peoplecopy()
{
    id = p.id,
    name = p.name,
    age = p.age
};
peoplecopy peoplecopy3 = func.invoke(people);





方法一:普通缓存
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
/// <summary>
/// 生成表达式目录树 缓存
/// </summary>
public class expressionmapper
{
    /// <summary>
    /// 字典缓存--hash分布
    /// </summary>
    private static dictionary<string, object> _dic = new dictionary<string, object>();

    /// <summary>
    /// 字典缓存表达式树
    /// </summary>
    /// <typeparam name="tin"></typeparam>
    /// <typeparam name="tout"></typeparam>
    /// <param name="tin"></param>
    /// <returns></returns>
    public static tout trans<tin, tout>(tin tin)
    {
      string key = string.format("funckey_{0}_{1}", typeof(tin).fullname, typeof(tout).fullname);
      if (!_dic.containskey(key))
      {
            parameterexpression parameterexpression = expression.parameter(typeof(tin), "p");
            list<memberbinding> memberbindinglist = new list<memberbinding>();
            foreach (var item in typeof(tout).getproperties())
            {
                memberexpression property = expression.property(parameterexpression, typeof(tin).getproperty(item.name));
                memberbinding memberbinding = expression.bind(item, property);
                memberbindinglist.add(memberbinding);
            }
            foreach (var item in typeof(tout).getfields())
            {
                memberexpression property = expression.field(parameterexpression, typeof(tin).getfield(item.name));
                memberbinding memberbinding = expression.bind(item, property);
                memberbindinglist.add(memberbinding);
            }
            memberinitexpression memberinitexpression = expression.memberinit(expression.new(typeof(tout)), memberbindinglist.toarray());
            expression<func<tin, tout>> lambda = expression.lambda<func<tin, tout>>(memberinitexpression, new parameterexpression[]
            {
                parameterexpression
            });
            func<tin, tout> func = lambda.compile();//拼装是一次性的
            _dic = func;
      }
      return ((func<tin, tout>)_dic).invoke(tin);
    }
}





方法二:泛型缓存,性能较高
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/// <summary>
/// 生成表达式目录树泛型缓存
/// </summary>
/// <typeparam name="tin"></typeparam>
/// <typeparam name="tout"></typeparam>
public class expressiongenericmapper<tin, tout>//mapper`2
{
    private static func<tin, tout> _func = null;
    static expressiongenericmapper()
    {
      parameterexpression parameterexpression = expression.parameter(typeof(tin), "p");
      list<memberbinding> memberbindinglist = new list<memberbinding>();
      foreach (var item in typeof(tout).getproperties())
      {
            memberexpression property = expression.property(parameterexpression, typeof(tin).getproperty(item.name));
            memberbinding memberbinding = expression.bind(item, property);
            memberbindinglist.add(memberbinding);
      }
      foreach (var item in typeof(tout).getfields())
      {
            memberexpression property = expression.field(parameterexpression, typeof(tin).getfield(item.name));
            memberbinding memberbinding = expression.bind(item, property);
            memberbindinglist.add(memberbinding);
      }
      memberinitexpression memberinitexpression = expression.memberinit(expression.new(typeof(tout)), memberbindinglist.toarray());
      expression<func<tin, tout>> lambda = expression.lambda<func<tin, tout>>(memberinitexpression, new parameterexpression[]
      {
                parameterexpression
      });
      _func = lambda.compile();//拼装是一次性的
    }
    public static tout trans(tin t)
    {
      return _func(t);
    }
}


本贴来源:帖子



页: [1]
查看完整版本: C# 表达式目录树Expression的实现