一切骗子与假货都是纸老虎 發表於 2019-9-15 17:33:00

C# 表达式树遍历(二)

<h2>一、前言</h2>
<p>上一篇我们对表达式树有了初步的认识,这里我们将对表达式树进行遍历,只有弄清楚了他的运行原理,我们才可以对他进行定制化修改。</p>
<p>表达式系列目录</p>
<p>C# 表达式树讲解(一)</p>
<p>C# 表达式树遍历(二)</p>
<p>&nbsp;C# 表达式树分页扩展(三)</p>
<p>C# 表达式树Lambda扩展(四)</p>
<h2>二、表达式树的遍历</h2>
<p>要查看表达式树的遍历,肯定不能直接用.Net Framework封装的方法,因为.Net Framework框架是闭源的,除了看中间语言(IL)去查看。我们就用ExpressionVisitor类查看一下他的运行原理,看了下ExpressionVisitor类,里面都是对各个表达式的访问,而且都是虚拟函数,我们可以对他进行override。</p>
<p>ExpressionVisitor类里面都是对各个类型的表达式进行访问,为了更好的理解里面的访问顺序,蜗牛把里面的虚函数都override了一遍,然后跟踪里面的执行顺序。【傻眼了,35个虚函数需要override,内心是很拒绝的,vs2019有没有重写父类虚函数的快捷键啊!!!!!!!】</p>
<p>ExpressionVisitor类相关介绍:https://docs.microsoft.com/zh-cn/dotnet/api/system.linq.expressions.expressionvisitor?view=netframework-4.8</p>
<h3>2.1、ExpressionVisitor类的跟踪</h3>
<p>为了不改变ExpressionVisitor类原来的访问,创建的SnailExpressionVisitor.cs 文件只在重写方法里面添加日志打印。</p>
<p>代码如下:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span> SnailExpressionVisitor : ExpressionVisitor
    {
      <span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">override</span> Expression Visit(Expression node)
      {
            Console.WriteLine($"<span style="color: rgba(139, 0, 0, 1)">访问了 Visit,内容:{node.ToString()}</span>");
            <span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">base</span>.Visit(node);
      }

      <span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">override</span> CatchBlock VisitCatchBlock(CatchBlock node)
      {

            Console.WriteLine($"<span style="color: rgba(139, 0, 0, 1)">访问了 VisitCatchBlock,内容:{node.ToString()}</span>");
            <span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">base</span>.VisitCatchBlock(node);
      }

      <span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">override</span> ElementInit VisitElementInit(ElementInit node)
      {
            Console.WriteLine($"<span style="color: rgba(139, 0, 0, 1)">访问了 VisitElementInit,内容:{node.ToString()}</span>");
            <span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">base</span>.VisitElementInit(node);
      }
      <span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">override</span> LabelTarget VisitLabelTarget(LabelTarget node)
      {

            Console.WriteLine($"<span style="color: rgba(139, 0, 0, 1)">访问了 VisitLabelTarget,内容:{node.ToString()}</span>");
            <span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">base</span>.VisitLabelTarget(node);
      }
      <span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">override</span> MemberAssignment VisitMemberAssignment(MemberAssignment node)
      {

            Console.WriteLine($"<span style="color: rgba(139, 0, 0, 1)">访问了 VisitMemberAssignment,内容:{node.ToString()}</span>");
            <span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">base</span>.VisitMemberAssignment(node);
      }
      <span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">override</span> MemberBinding VisitMemberBinding(MemberBinding node)
      {

            Console.WriteLine($"<span style="color: rgba(139, 0, 0, 1)">访问了 VisitMemberBinding,内容:{node.ToString()}</span>");
            <span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">base</span>.VisitMemberBinding(node);
      }

      <span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">override</span> MemberListBinding VisitMemberListBinding(MemberListBinding node)
      {

            Console.WriteLine($"<span style="color: rgba(139, 0, 0, 1)">访问了 VisitMemberListBinding,内容:{node.ToString()}</span>");
            <span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">base</span>.VisitMemberListBinding(node);
      }
      <span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">override</span> MemberMemberBinding VisitMemberMemberBinding(MemberMemberBinding node)
      {

            Console.WriteLine($"<span style="color: rgba(139, 0, 0, 1)">访问了 VisitMemberMemberBinding,内容:{node.ToString()}</span>");
            <span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">base</span>.VisitMemberMemberBinding(node);
      }
      <span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">override</span> SwitchCase VisitSwitchCase(SwitchCase node)
      {
            Console.WriteLine($"<span style="color: rgba(139, 0, 0, 1)">访问了 VisitSwitchCase,内容:{node.ToString()}</span>");
            <span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">base</span>.VisitSwitchCase(node);
      }
      <span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">override</span> Expression VisitBinary(BinaryExpression node)
      {
            Console.WriteLine($"<span style="color: rgba(139, 0, 0, 1)">访问了 VisitBinary,内容:{node.ToString()}</span>");
            <span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">base</span>.VisitBinary(node);
      }
      <span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">override</span> Expression VisitBlock(BlockExpression node)
      {
            Console.WriteLine($"<span style="color: rgba(139, 0, 0, 1)">访问了 VisitBlock,内容:{node.ToString()}</span>");
            <span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">base</span>.VisitBlock(node);
      }

      <span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">override</span> Expression VisitConditional(ConditionalExpression node)
      {
            Console.WriteLine($"<span style="color: rgba(139, 0, 0, 1)">访问了 VisitConditional,内容:{node.ToString()}</span>");
            <span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">base</span>.VisitConditional(node);
      }

      <span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">override</span> Expression VisitConstant(ConstantExpression node)
      {
            Console.WriteLine($"<span style="color: rgba(139, 0, 0, 1)">访问了 VisitConstant,内容:{node.ToString()}</span>");
            <span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">base</span>.VisitConstant(node);
      }
      <span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">override</span> Expression VisitDebugInfo(DebugInfoExpression node)
      {
            Console.WriteLine($"<span style="color: rgba(139, 0, 0, 1)">访问了 VisitDebugInfo,内容:{node.ToString()}</span>");
            <span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">base</span>.VisitDebugInfo(node);
      }
      <span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">override</span> Expression VisitDefault(DefaultExpression node)
      {
            Console.WriteLine($"<span style="color: rgba(139, 0, 0, 1)">访问了 VisitDefault,内容:{node.ToString()}</span>");
            <span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">base</span>.VisitDefault(node);
      }

      <span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">override</span> Expression VisitDynamic(DynamicExpression node)
      {
            Console.WriteLine($"<span style="color: rgba(139, 0, 0, 1)">访问了 VisitDynamic,内容:{node.ToString()}</span>");
            <span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">base</span>.VisitDynamic(node);
      }
      <span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">override</span> Expression VisitExtension(Expression node)
      {
            Console.WriteLine($"<span style="color: rgba(139, 0, 0, 1)">访问了 VisitExtension,内容:{node.ToString()}</span>");
            <span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">base</span>.VisitExtension(node);
      }
      <span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">override</span> Expression VisitGoto(GotoExpression node)
      {
            Console.WriteLine($"<span style="color: rgba(139, 0, 0, 1)">访问了 VisitGoto,内容:{node.ToString()}</span>");
            <span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">base</span>.VisitGoto(node);
      }
      <span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">override</span> Expression VisitIndex(IndexExpression node)
      {
            Console.WriteLine($"<span style="color: rgba(139, 0, 0, 1)">访问了 VisitIndex,内容:{node.ToString()}</span>");
            <span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">base</span>.VisitIndex(node);
      }
      <span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">override</span> Expression VisitInvocation(InvocationExpression node)
      {
            Console.WriteLine($"<span style="color: rgba(139, 0, 0, 1)">访问了 VisitInvocation,内容:{node.ToString()}</span>");
            <span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">base</span>.VisitInvocation(node);
      }
      <span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">override</span> Expression VisitLabel(LabelExpression node)
      {
            Console.WriteLine($"<span style="color: rgba(139, 0, 0, 1)">访问了 VisitLabel,内容:{node.ToString()}</span>");
            <span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">base</span>.VisitLabel(node);
      }
      <span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">override</span> Expression VisitLambda&lt;T&gt;(Expression&lt;T&gt; node)
      {
            Console.WriteLine($"<span style="color: rgba(139, 0, 0, 1)">访问了 VisitLambda,内容:{node.ToString()}</span>");
            <span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">base</span>.VisitLambda(node);
      }

      <span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">override</span> Expression VisitListInit(ListInitExpression node)
      {
            Console.WriteLine($"<span style="color: rgba(139, 0, 0, 1)">访问了 VisitListInit,内容:{node.ToString()}</span>");
            <span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">base</span>.VisitListInit(node);
      }
      <span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">override</span> Expression VisitLoop(LoopExpression node)
      {
            Console.WriteLine($"<span style="color: rgba(139, 0, 0, 1)">访问了 VisitLoop,内容:{node.ToString()}</span>");
            <span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">base</span>.VisitLoop(node);
      }
      <span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">override</span> Expression VisitMember(MemberExpression node)
      {
            Console.WriteLine($"<span style="color: rgba(139, 0, 0, 1)">访问了 VisitMember,内容:{node.ToString()}</span>");
            <span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">base</span>.VisitMember(node);
      }
      <span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">override</span> Expression VisitMemberInit(MemberInitExpression node)
      {
            Console.WriteLine($"<span style="color: rgba(139, 0, 0, 1)">访问了 VisitMemberInit,内容:{node.ToString()}</span>");
            <span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">base</span>.VisitMemberInit(node);
      }
      <span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">override</span> Expression VisitMethodCall(MethodCallExpression node)
      {
            Console.WriteLine($"<span style="color: rgba(139, 0, 0, 1)">访问了 VisitMethodCall,内容:{node.ToString()}</span>");
            <span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">base</span>.VisitMethodCall(node);
      }
      <span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">override</span> Expression VisitNew(NewExpression node)
      {
            Console.WriteLine($"<span style="color: rgba(139, 0, 0, 1)">访问了 VisitNew,内容:{node.ToString()}</span>");
            <span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">base</span>.VisitNew(node);
      }
      <span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">override</span> Expression VisitNewArray(NewArrayExpression node)
      {
            Console.WriteLine($"<span style="color: rgba(139, 0, 0, 1)">访问了 VisitNewArray,内容:{node.ToString()}</span>");
            <span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">base</span>.VisitNewArray(node);
      }

      <span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">override</span> Expression VisitParameter(ParameterExpression node)
      {
            Console.WriteLine($"<span style="color: rgba(139, 0, 0, 1)">访问了 VisitParameter,内容:{node.ToString()}</span>");
            <span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">base</span>.VisitParameter(node);
      }
      <span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">override</span> Expression VisitRuntimeVariables(RuntimeVariablesExpression node)
      {
            Console.WriteLine($"<span style="color: rgba(139, 0, 0, 1)">访问了 VisitRuntimeVariables,内容:{node.ToString()}</span>");
            <span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">base</span>.VisitRuntimeVariables(node);
      }

      <span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">override</span> Expression VisitSwitch(SwitchExpression node)
      {
            Console.WriteLine($"<span style="color: rgba(139, 0, 0, 1)">访问了 VisitSwitch,内容:{node.ToString()}</span>");
            <span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">base</span>.VisitSwitch(node);
      }
      <span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">override</span> Expression VisitTry(TryExpression node)
      {
            Console.WriteLine($"<span style="color: rgba(139, 0, 0, 1)">访问了 VisitTry,内容:{node.ToString()}</span>");
            <span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">base</span>.VisitTry(node);
      }

      <span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">override</span> Expression VisitTypeBinary(TypeBinaryExpression node)
      {
            Console.WriteLine($"<span style="color: rgba(139, 0, 0, 1)">访问了 VisitTypeBinary,内容:{node.ToString()}</span>");
            <span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">base</span>.VisitTypeBinary(node);
      }
      <span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">override</span> Expression VisitUnary(UnaryExpression node)
      {
            Console.WriteLine($"<span style="color: rgba(139, 0, 0, 1)">访问了 VisitUnary,内容:{node.ToString()}</span>");
            <span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">base</span>.VisitUnary(node);
      }



    }</pre>
</div>
<p>调用方法:</p>
<div class="cnblogs_code">
<pre>Expression&lt;Func&lt;<span style="color: rgba(0, 0, 255, 1)">int</span>, <span style="color: rgba(0, 0, 255, 1)">int</span>, <span style="color: rgba(0, 0, 255, 1)">bool</span>&gt;&gt; fun = (x, y) =&gt; x - y &gt; 5;

var treeModifier = <span style="color: rgba(0, 0, 255, 1)">new</span> SnailExpressionVisitor();
Expression modifiedExpr = treeModifier.Visit(fun);
</pre>
</div>
<p>运行结果:</p>
<p><img style="display: inline; background-image: none" title="image" src="https://img2018.cnblogs.com/blog/1764554/201909/1764554-20190915102305597-949925908.png" alt="image" width="592" height="425" border="0"></p>
<p>从打印的日志里面可以看出,</p>
<p>1、每次访问表达式类时,都会先去调用Visit函数,估计他是在Visit里面判定表达式类,然后在根据表达式类的类型,调用访问改表达式的函数</p>
<p>2、对Lambda表达式类,是先访问的是Expression&lt;T&gt;。Expression&lt;T&gt;是不是很熟悉,上一章说过他的作用是将强类型Lambda表达式表示为表达式树形式的数据结构,解析成功之后才对表达式的访问</p>
<p>3、对于表达式先解析的是左边,左边的内容解析完了之后在解析右边,如(x-y)&gt;5,解析的顺序是:x-y=&gt;x=&gt;y=&gt;5</p>
<h3>2.2、修改表达式树</h3>
<p>既然我们弄清楚了表达式树的访问,现在我们就可以对他进行编辑修改了。</p>
<p>上面我们判断的是x-y&gt;5,现在我们规定,将“-”改成“+”,“&gt;”改成“&gt;=”</p>
<p>对VisitBinary方法修改代码如下:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">override</span> Expression VisitBinary(BinaryExpression node)
{
    Console.WriteLine($"<span style="color: rgba(139, 0, 0, 1)">访问了 VisitBinary,内容:{node.ToString()}</span>");
    <span style="color: rgba(0, 0, 255, 1)">if</span> (node.NodeType == ExpressionType.GreaterThan)
    {
      Expression left = <span style="color: rgba(0, 0, 255, 1)">this</span>.Visit(node.Left);
      Expression right = <span style="color: rgba(0, 0, 255, 1)">this</span>.Visit(node.Right);

      var result = Expression.MakeBinary(ExpressionType.GreaterThanOrEqual, left, right, node.IsLiftedToNull, node.Method);
      Console.WriteLine($"<span style="color: rgba(139, 0, 0, 1)">访问了 VisitBinary,更改之后的内容:{result.ToString()}</span>");
      <span style="color: rgba(0, 0, 255, 1)">return</span> result;
    }
    <span style="color: rgba(0, 0, 255, 1)">else</span> <span style="color: rgba(0, 0, 255, 1)">if</span> (node.NodeType == ExpressionType.Subtract || node.NodeType == ExpressionType.SubtractChecked)
    {
      Expression left = <span style="color: rgba(0, 0, 255, 1)">this</span>.Visit(node.Left);
      Expression right = <span style="color: rgba(0, 0, 255, 1)">this</span>.Visit(node.Right);

      var result = Expression.MakeBinary(ExpressionType.Add, left, right, node.IsLiftedToNull, node.Method);
      Console.WriteLine($"<span style="color: rgba(139, 0, 0, 1)">访问了 VisitBinary,更改之后的内容:{result.ToString()}</span>");
      <span style="color: rgba(0, 0, 255, 1)">return</span> result;
    }
    <span style="color: rgba(0, 0, 255, 1)">else</span>
    {
      <span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">base</span>.VisitBinary(node);
    }
}
</pre>
</div>
<p>调用方法:</p>
<div class="cnblogs_code">
<pre>Expression&lt;Func&lt;<span style="color: rgba(0, 0, 255, 1)">int</span>, <span style="color: rgba(0, 0, 255, 1)">int</span>, <span style="color: rgba(0, 0, 255, 1)">bool</span>&gt;&gt; fun = (x, y) =&gt; x - y &gt; 5;

var treeModifier = <span style="color: rgba(0, 0, 255, 1)">new</span> SnailExpressionVisitor();
Expression modifiedExpr = treeModifier.Visit(fun);

Console.WriteLine($"<span style="color: rgba(139, 0, 0, 1)">Lambda的转换最后结果:{modifiedExpr.ToString()}</span>");
</pre>
</div>
<p>运行结果如下</p>
<p><img style="display: inline; background-image: none" title="image" src="https://img2018.cnblogs.com/blog/1764554/201909/1764554-20190915102306466-944333548.png" alt="image" width="572" height="403" border="0"></p>
<h2>三、总结</h2>
<p>对表达树的讲解已经完成了,但是说了这么久,对真实的开发有什么作用呢?后面我将利用Lambda表达式写一个对现有数据分页的公共方法,同时在对Dapper的扩展也会用到相关知识点,大家拭目以待吧……</p>

</div>
<div id="MySignature" role="contentinfo">
    <div style="font-size: 13px; border: 1px dashed rgb(45, 161, 45); padding: 10px 15px; background-color: rgb(248, 248, 248)">
<label style="font-weight: bold">
&nbsp;&nbsp;&nbsp;&nbsp;如果您觉得阅读本文对您有帮助,请点一下右下角推荐”按钮,博主在此感谢!另外您也可以选择【<strong>关注我</strong>】,可以很方便找到我!</label><br>
&nbsp;&nbsp;&nbsp;&nbsp;感谢您花时间阅读此篇文章,如果您觉得看了这篇文章之后心情还比较高兴,可以打赏一下,请博主喝上一杯咖啡,让博主继续码字……<br>
&nbsp;&nbsp;&nbsp;&nbsp;本文版权归作者和博客园共有,来源网址:https://www.cnblogs.com/snailblog 欢迎各位转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接
</div><br><br>
来源:https://www.cnblogs.com/snailblog/p/11521335.html
頁: [1]
查看完整版本: C# 表达式树遍历(二)