From 699a7321ed32e24a45d37e73f81de5808aaee9d8 Mon Sep 17 00:00:00 2001 From: jackletter <1286317554@qq.com> Date: Sat, 1 Jul 2023 23:49:57 +0800 Subject: [PATCH] =?UTF-8?q?DotNetCommon.core=20v4.2.0=20ExpressionHelper.R?= =?UTF-8?q?educeLambda=20=E6=96=B9=E6=B3=95=E6=96=B0=E5=A2=9E=20isKeep=20?= =?UTF-8?q?=E5=8F=82=E6=95=B0,=E6=96=B9=E4=BE=BF=E7=AE=80=E5=8C=96?= =?UTF-8?q?=E6=97=B6=E4=BF=9D=E7=95=99=E4=B8=80=E4=BA=9B=E8=8A=82=E7=82=B9?= =?UTF-8?q?,=E6=AF=94=E5=A6=82=20new{...}?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DotNetCommon.Core.csproj | 2 +- src/DotNetCommon.Core/ExpressionHelper.cs | 5 +- .../Expressions/Base/BaseVisit.cs | 81 +++++++++++-------- .../Expressions/Visit/VisitImplements.cs | 4 +- .../ReduceLambdaTests.cs | 42 ++++++++++ 5 files changed, 95 insertions(+), 39 deletions(-) diff --git a/src/DotNetCommon.Core/DotNetCommon.Core.csproj b/src/DotNetCommon.Core/DotNetCommon.Core.csproj index 3989557..e361914 100644 --- a/src/DotNetCommon.Core/DotNetCommon.Core.csproj +++ b/src/DotNetCommon.Core/DotNetCommon.Core.csproj @@ -1,7 +1,7 @@ - 4.1.1 + 4.2.0 True .net常用功能及数据模型,包含: diff --git a/src/DotNetCommon.Core/ExpressionHelper.cs b/src/DotNetCommon.Core/ExpressionHelper.cs index e3f9f9f..e1e5aab 100644 --- a/src/DotNetCommon.Core/ExpressionHelper.cs +++ b/src/DotNetCommon.Core/ExpressionHelper.cs @@ -165,6 +165,7 @@ namespace DotNetCommon /// /// /// + /// 是否保留此表达式,比如: 在 根据lambda解析生成update语句的时候,期望保留 new Person{} /// /// 简化的原理:
/// @@ -175,7 +176,7 @@ namespace DotNetCommon /// ///
/// - public static Expression ReduceLambda(LambdaExpression lambdaExpression) + public static Expression ReduceLambda(LambdaExpression lambdaExpression, Func isKeepCallBack = null) { var root = new ExpressionNode { @@ -198,7 +199,7 @@ namespace DotNetCommon { if (node.Expression == null) return; var visit = GetVisit(node.NodeType); - visit.Reduce(node, VisitTree); + visit.Reduce(node, VisitTree, isKeepCallBack); } } private static Dictionary _caches = new Dictionary() diff --git a/src/DotNetCommon.Core/Expressions/Base/BaseVisit.cs b/src/DotNetCommon.Core/Expressions/Base/BaseVisit.cs index d6c5131..881b03e 100644 --- a/src/DotNetCommon.Core/Expressions/Base/BaseVisit.cs +++ b/src/DotNetCommon.Core/Expressions/Base/BaseVisit.cs @@ -34,12 +34,15 @@ namespace DotNetCommon.Expressions.Base /// /// /// - public void Reduce(ExpressionNode node, Action visit) + /// 是否保留此表达式,比如: 在 根据lambda解析生成update语句的时候,期望保留 new Person{} + public void Reduce(ExpressionNode node, Action visit, Func isKeepCallBack) { Prepare(node); node.Children.ForEach(x => visit(x)); node.FullMarkString = GenerateFullMarkString(node); - SimpleReduce(node); + var isKeep = false; + if (isKeepCallBack != null) isKeep = isKeepCallBack(node.Expression); + SimpleReduce(node, isKeep); Reduce2(node); } /// @@ -57,48 +60,58 @@ namespace DotNetCommon.Expressions.Base /// 这样, 不含参数的子节点将被简化, 如果所有子节点都不含参数, 那说明简化的时机还没到, 延迟到父节点进行判断 /// /// - private void SimpleReduce(ExpressionNode node) + /// + private void SimpleReduce(ExpressionNode node, bool isKeep) { - //判断是否能简化 - if (node.Children.Count > 1) + var canReduce = false; + if (isKeep) { - var canReduce = false; - for (int i = 0; i < node.Children.Count - 1; i++) + canReduce = true; + //hack 将自身标记为 parameter 防止被上级简化 + node.IsParameter = true; + } + else + { + if (node.Children.Count > 1) { - if (node.Children[i].HasParameter ^ node.Children[i + 1].HasParameter) + for (int i = 0; i < node.Children.Count - 1; i++) { - //不相同 要进行简化 - canReduce = true; - break; + if (node.Children[i].HasParameter ^ node.Children[i + 1].HasParameter) + { + //不相同 要进行简化 + canReduce = true; + break; + } } + } - if (canReduce) + } + if (canReduce) + { + for (int i = 0; i < node.Children.Count; i++) { - for (int i = 0; i < node.Children.Count; i++) + var child = node.Children[i]; + if (!child.HasParameter && child.Expression != null) { - var child = node.Children[i]; - if (!child.HasParameter && child.Expression != null) + if (child.NodeType == ExpressionType.Lambda) { - if (child.NodeType == ExpressionType.Lambda) - { - //lambda表达式节点不能再进一步简化,最多将它的body简化成常量 - //因为有的方法本身接受的就是lambda表达式 - node.Children[i]._updateRequest = true; - //node.Children[i].FullMarkString = GenerateFullMarkString(node.Children[i]); - } - else if (child.NodeType != ExpressionType.Constant) + //lambda表达式节点不能再进一步简化,最多将它的body简化成常量 + //因为有的方法本身接受的就是lambda表达式 + node.Children[i]._updateRequest = true; + //node.Children[i].FullMarkString = GenerateFullMarkString(node.Children[i]); + } + else if (child.NodeType != ExpressionType.Constant) + { + //进行简化 + var res = child.Reduce(); + var contant = Expression.Constant(res, child.Expression.Type); + node.Children[i] = new ExpressionNode { - //进行简化 - var res = child.Reduce(); - var contant = Expression.Constant(res, child.Expression.Type); - node.Children[i] = new ExpressionNode - { - Parent = node, - Expression = contant, - _updateRequest = true, - }; - node.Children[i].FullMarkString = ConstantVisit.StaticGenerateFullMarkString(node.Children[i]); - } + Parent = node, + Expression = contant, + _updateRequest = true, + }; + node.Children[i].FullMarkString = ConstantVisit.StaticGenerateFullMarkString(node.Children[i]); } } } diff --git a/src/DotNetCommon.Core/Expressions/Visit/VisitImplements.cs b/src/DotNetCommon.Core/Expressions/Visit/VisitImplements.cs index 6bddd5f..e88302d 100644 --- a/src/DotNetCommon.Core/Expressions/Visit/VisitImplements.cs +++ b/src/DotNetCommon.Core/Expressions/Visit/VisitImplements.cs @@ -637,9 +637,9 @@ namespace DotNetCommon.Expressions.Visit //先更新New newExpression = newExpression.Update(node.Children.Take(memberSetIndex).Select(i => i.Expression)); //再更新ListInit - for (int i = 1; i < node.Children.Count; i++) + for (int i = memberSetIndex; i < node.Children.Count; i++) { - inits.Add(listInitExpression.Initializers[i - 1].Update(new[] { node.Children[i].Expression })); + inits.Add(listInitExpression.Initializers[i - memberSetIndex].Update(new[] { node.Children[i].Expression })); } return listInitExpression.Update(newExpression, inits); } diff --git a/tests/DotNetCommon.Test/ExpressionHelperTests/ReduceLambdaTests.cs b/tests/DotNetCommon.Test/ExpressionHelperTests/ReduceLambdaTests.cs index 99e208a..90cfc7b 100644 --- a/tests/DotNetCommon.Test/ExpressionHelperTests/ReduceLambdaTests.cs +++ b/tests/DotNetCommon.Test/ExpressionHelperTests/ReduceLambdaTests.cs @@ -427,6 +427,48 @@ namespace DotNetCommon.Test.ExpressionHelperTests res.FirstOrDefault().Age.ShouldBe(18); } #endregion + + #region canReduce + class Person8 + { + public int Id { get; set; } + public string Name { get; set; } + public List Children { get; set; } + } + + + [Test] + public void IsKeepTest() + { + var names = new List() { "tom", "lisa" }; + Expression> lambda = p => new Person8 + { + Id = p.Id + 1, + Name = names.FirstOrDefault(), + Children = new List + { + new Person8() { Id = names.Count}, + p + } + }; + var res = ExpressionHelper.ReduceLambda(lambda, exp => exp.NodeType == ExpressionType.MemberInit); + res.ToString().Replace("\r\n", "").ShouldBe(@"p => new Person8() {Id = (p.Id + 1), Name = ""tom"", Children = new List`1() {Void Add(Person8)(new Person8() {Id = 2}), Void Add(Person8)(p)}}".Replace("\r\n", "")); + + lambda = p => new Person8 + { + Id = p.Id + 1, + Name = names.FirstOrDefault(), + Children = new List + { + new Person8() { Id = names.Count}, + new Person8() { Id = names.Count}, + } + }; + res = ExpressionHelper.ReduceLambda(lambda, exp => exp.NodeType == ExpressionType.ListInit); + res.ToString().Replace("\r\n", "").ShouldBe(@"p => new Person8() {Id = (p.Id + 1), Name = ""tom"", Children = new List`1() {Void Add(Person8)(value(DotNetCommon.Test.ExpressionHelperTests.EveryNodeTypeTests+Person8)), Void Add(Person8)(value(DotNetCommon.Test.ExpressionHelperTests.EveryNodeTypeTests+Person8))}}".Replace("\r\n", "")); + } + + #endregion } #endregion -- Gitee