表達式樹練習實踐:C# 迴圈與迴圈控制

来源:https://www.cnblogs.com/whuanle/archive/2019/09/20/11559795.html
-Advertisement-
Play Games

表達式樹練習實踐:C 迴圈 [TOC] C 提供了以下幾種迴圈類型。 | 迴圈類型 | 描述 | | : | : | | while 迴圈 | 當給定條件為真時,重覆語句或語句組。它會在執行迴圈主體之前測試條件。 | | for/foreach 迴圈 | 多次執行一個語句序列,簡化管理迴圈變數的代碼 ...


目錄

表達式樹練習實踐:C# 迴圈

C# 提供了以下幾種迴圈類型。

迴圈類型 描述
while 迴圈 當給定條件為真時,重覆語句或語句組。它會在執行迴圈主體之前測試條件。
for/foreach 迴圈 多次執行一個語句序列,簡化管理迴圈變數的代碼。
do...while 迴圈 除了它是在迴圈主體結尾測試條件外,其他與 while 語句類似。
嵌套迴圈 您可以在 while、for 或 do..while 迴圈內使用一個或多個迴圈。

當然,還有以下用於控制迴圈的語句

控制語句 描述
break 語句 終止 loopswitch 語句,程式流將繼續執行緊接著 loop 或 switch 的下一條語句。
continue 語句 引起迴圈跳過主體的剩餘部分,立即重新開始測試條件。

LabelTarget

LabelTarget 是用於創建迴圈標記的。

無論是 for 還是 while ,平時編寫迴圈時,都需要有跳出迴圈的判斷,有時需要某個參數自增自減並且作為判斷依據。

C# 表達式樹裡面是沒有專門表示 for /while 的,裡面只有一個 Loop。看一下Loop 生成的表達式樹

.Lambda #Lambda1<System.Func`1[System.Int32]>() {
    .Block(System.Int32 $x) {
        $x = 0;
        .Loop  {
            .If ($x < 10) {
                $x++
            } .Else {
                .Break #Label1 { $x }
            }
        }
        .LabelTarget #Label1:
    }
}

要實現迴圈控制,有 break,contauine 兩種 Expression:

        public static GotoExpression Break(LabelTarget target, Type type);

        public static GotoExpression Break(LabelTarget target, Expression value);

        public static GotoExpression Break(LabelTarget target);

        public static GotoExpression Break(LabelTarget target, Expression value, Type type);
        public static GotoExpression Continue(LabelTarget target, Type type);

        public static GotoExpression Continue(LabelTarget target);

所以,要實現迴圈控制,必須要使用 LabelTarget,不然就無限迴圈了。

要理解 LabelTarget ,最好的方法是動手做。

for / while 迴圈

Expression.Loop 用於創建迴圈,包括 for 和 while,定義如下

        public static LoopExpression Loop(Expression body, LabelTarget @break, LabelTarget @continue);
        
      System.Linq.Expressions.LoopExpression.
        public static LoopExpression Loop(Expression body);
      
        public static LoopExpression Loop(Expression body, LabelTarget @break);

表達式樹裡面的迴圈,只有 Loop,無 for / while 的區別。

那麼,我們來一步步理解 Loop 迴圈和 LabelTarget;

無限迴圈

                while (true)
                {
                    Console.WriteLine("無限迴圈");
                }

那麼,對應的 Loop 重載是這種

public static LoopExpression Loop(Expression body)

使用表達式樹編寫

            BlockExpression _block = Expression.Block(
                new ParameterExpression[] { },
                Expression.Call(null, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }),Expression.Constant("無限迴圈") )
            );

            LoopExpression _loop = Expression.Loop(_block);

            Expression<Action> lambda = Expression.Lambda<Action>(_loop);
            lambda.Compile()();

最簡單的迴圈

如果我想用表達式樹做到如下最簡單的迴圈,怎麼寫?

            while (true)
            {
                Console.WriteLine("我被執行一次就結束迴圈了");
                break;
            }

表達式樹編寫

            LabelTarget _break = Expression.Label();

            BlockExpression _block = Expression.Block(
               new ParameterExpression[] { },
               Expression.Call(null, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }), Expression.Constant("我被執行一次就結束迴圈了")), Expression.Break(_break));
            LoopExpression _loop = Expression.Loop(_block, _break);

            Expression<Action> lambda = Expression.Lambda<Action>(_loop);
            lambda.Compile()();

            Console.ReadKey();

生成的表達式樹

.Lambda #Lambda1<System.Action>() {
    .Loop  {
        .Block() {
            .Call System.Console.WriteLine("我被執行一次就結束迴圈了");
            .Break #Label1 { }
        }
    }
    .LabelTarget #Label1:
}

首先要明確,Expression.Label() 裡面可以為空,它是一種標記,不參與傳遞參數,不參與運算。有參無參,前後保持一致即可。

但是上面的迴圈只有一次,你可以將上面的標簽改成這樣試試 LabelTarget _break = Expression.Label(typeof(int));,原因後面找。

還有, Expression.Label() 變數需要一致,否則無法跳出。

試試一下代碼

            BlockExpression _block = Expression.Block(
               new ParameterExpression[] { },
               Expression.Call(null, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }), Expression.Constant("我被執行一次就結束迴圈了")), Expression.Break(Expression.Label()));
            LoopExpression _loop = Expression.Loop(_block, Expression.Label());

            Expression<Action> lambda = Expression.Lambda<Action>(_loop);
            lambda.Compile()();

            Console.ReadKey();

裡面用到了 Expression.Block(),Block() 是塊,即{}。

如果 Block() 是在最外層,那麼相當於是函數;如果是內嵌,相當於{};

但不是真的這樣。。。表達式樹裡面不是完全按照 C# 的語法來還原操作的。

對於 Block() 的使用,多加實踐即可。

多次迴圈

寫一個迴圈十次的迴圈語句

            for (int i = 0; i < 10; i++)
            {
                if (i < 10)
                {
                    Console.WriteLine(i);
                }
                else
                    break;
            }

或者使用 while 表示

            int i = 0;
            while (true)
            {
                if (i < 10)
                {
                    Console.WriteLine(i);
                }
                else
                    break;
                i++;
            }

使用表達式樹編寫

            LabelTarget _break = Expression.Label(typeof(int));
            ParameterExpression a = Expression.Variable(typeof(int), "a");

            BlockExpression _block = Expression.Block(new ParameterExpression[] { },
                Expression.IfThenElse
                (
                    Expression.LessThan(a, Expression.Constant(10)),
                    Expression.Call(null, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(int) }), a),
                    Expression.Break(_break, a)
                ),
                Expression.PostIncrementAssign(a)   // a++
                );


            LoopExpression _loop = Expression.Loop(_block, _break);

            Expression<Action<int>> lambda = Expression.Lambda<Action<int>>(_loop, a);
            lambda.Compile()(0);
            Console.ReadKey();

生成的表達式樹如下

.Lambda #Lambda1<System.Action`1[System.Int32]>(System.Int32 $a) {
    .Loop  {
        .Block() {
            .If ($a < 10) {
                .Call System.Console.WriteLine($a)
            } .Else {
                .Break #Label1 { $a }
            };
            $a++
        }
    }
    .LabelTarget #Label1:
}

試試將 Expression.Break(_break, a) 改成 Expression.Break(_break)。看看報什麼錯。。。

解決方法是,上面的標記也改成 LabelTarget _break = Expression.Label();

就跟你寫代碼寫註釋一樣,裡面的東西是為了讓別人看代碼是容易理解。

有些同學糾結於 Expression.Label(有參或無參);Expression.Break(_break, a)Expression.Break(_break),只要看看最終生成的表達式樹就清楚了。

break 和 continue 一起

C# 迴圈代碼如下

            int i = 0;
            while (true)
            {
                if (i < 10)
                {
                    if (i % 2 == 0)
                    {
                        Console.Write("i是偶數:");
                        Console.WriteLine(i);
                        i++;
                        continue;
                    }
                    Console.WriteLine("其他任務 --");
                    Console.WriteLine("其他任務 --");
                }
                else break;
                i++;
            }

使用 C# 表達式樹編寫(筆者將步驟詳細拆分了,所以代碼比較長)

            ParameterExpression a = Expression.Variable(typeof(int), "a");

            LabelTarget _break = Expression.Label();
            LabelTarget _continue = Expression.Label();

            //        if (i % 2 == 0)
            //        {
            //            Console.Write("i是偶數:");
            //            Console.WriteLine(i);
            //            i++;
            //            continue;
            //        }
            ConditionalExpression _if = Expression.IfThen(
                Expression.Equal(Expression.Modulo(a, Expression.Constant(2)), Expression.Constant(0)),
                Expression.Block(
                    new ParameterExpression[] { },
                    Expression.Call(null, typeof(Console).GetMethod("Write", new Type[] { typeof(string) }), Expression.Constant("i是偶數:")),
                    Expression.Call(null, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(int) }), a),
                    Expression.PostIncrementAssign(a),
                    Expression.Continue(_continue)
                    )
                );

            //        if (i % 2 == 0)
            //        {
            //            Console.Write("i是偶數:");
            //            Console.WriteLine(i);
            //            i++;
            //            continue;
            //        }
            //        Console.WriteLine("其他任務 --");
            //        Console.WriteLine("其他任務 --");
            BlockExpression block1 = Expression.Block(
                new ParameterExpression[] { },
                _if,
                Expression.Call(null, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }), Expression.Constant("其他任務 --")),
                Expression.Call(null, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }), Expression.Constant("其他任務 --"))
                );

            //    if (i < 10)
            //    {
            //        if (i % 2 == 0)
            //        {
            //            Console.Write("i是偶數:");
            //            Console.WriteLine(i);
            //            i++;
            //            continue;
            //        }
            //        Console.WriteLine("其他任務 --");
            //        Console.WriteLine("其他任務 --");
            //    }
            //    else break;
            ConditionalExpression if_else = Expression.IfThenElse(
               Expression.LessThan(a, Expression.Constant(10)),
                block1,
                Expression.Break(_break)
                );


            //    if (i < 10)
            //    {
            //        if (i % 2 == 0)
            //        {
            //            Console.Write("i是偶數:");
            //            Console.WriteLine(i);
            //            i++;
            //            continue;
            //        }
            //        Console.WriteLine("其他任務 --");
            //        Console.WriteLine("其他任務 --");
            //    }
            //    else break;
            //    i++ ;

            BlockExpression block2 = Expression.Block(
                new ParameterExpression[] { },
                if_else,
                Expression.PostIncrementAssign(a)
                );
            // while(true)
            LoopExpression loop = Expression.Loop(block2, _break, _continue);

            Expression<Action<int>> lambda = Expression.Lambda<Action<int>>(loop, a);
            lambda.Compile()(0);
            Console.ReadKey();

生成的表達式樹如下

.Lambda #Lambda1<System.Action`1[System.Int32]>(System.Int32 $a) {
    .Loop .LabelTarget #Label1: {
        .Block() {
            .If ($a < 10) {
                .Block() {
                    .If (
                        $a % 2 == 0
                    ) {
                        .Block() {
                            .Call System.Console.Write("i是偶數:");
                            .Call System.Console.WriteLine($a);
                            $a++;
                            .Continue #Label1 { }
                        }
                    } .Else {
                        .Default(System.Void)
                    };
                    .Call System.Console.WriteLine("其他任務 --");
                    .Call System.Console.WriteLine("其他任務 --")
                }
            } .Else {
                .Break #Label2 { }
            };
            $a++
        }
    }
    .LabelTarget #Label2:
}

為了便於理解,上面的代碼拆分了很多步。

來個簡化版本

            ParameterExpression a = Expression.Variable(typeof(int), "a");

            LabelTarget _break = Expression.Label();
            LabelTarget _continue = Expression.Label();

            LoopExpression loop = Expression.Loop(
                Expression.Block(
                    new ParameterExpression[] { },
                    Expression.IfThenElse(
                        Expression.LessThan(a, Expression.Constant(10)),
                        Expression.Block(
                            new ParameterExpression[] { },
                            Expression.IfThen(
                                Expression.Equal(Expression.Modulo(a, Expression.Constant(2)), Expression.Constant(0)),
                                Expression.Block(
                                    new ParameterExpression[] { },
                                    Expression.Call(null, typeof(Console).GetMethod("Write", new Type[] { typeof(string) }), Expression.Constant("i是偶數:")),
                                    Expression.Call(null, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(int) }), a),
                                    Expression.PostIncrementAssign(a),
                                    Expression.Continue(_continue)
                                    )
                                ),
                            Expression.Call(null, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }), Expression.Constant("其他任務 --")),
                            Expression.Call(null, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }), Expression.Constant("其他任務 --"))
                            ),
                        Expression.Break(_break)
                        ),
                    Expression.PostIncrementAssign(a)
                    ),
                _break,
                _continue
                );

            Expression<Action<int>> lambda = Expression.Lambda<Action<int>>(loop, a);
            lambda.Compile()(0);
            Console.ReadKey();

需要註意的是,Expression.Break Expression.Continue 有所區別。

當標簽實例化都是 Expression.Label() 時,

Expression.Break(label);
Expression.Continu(label);

區別在於 continu 只能用 Expression.Label()。

Break 可以這樣

LabelTarget label = Expression.Label ( typeof ( int ) );
ParameterExpression a = Expression.Variable(typeof(int), "a");

Expression.Break ( label , a ) 

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 由於公司業務要求,西瓜代理已經不滿足需求,準備更換新的代理IP池,所以調研測試了一下市面上的各家付費代理(免費代理可用率低故不考慮),功能限制和價格情況等如何,以便從中挑選滿足要求的代理。 1、目標站 2、情報收集 整理套餐的價格和類型,API頻率,每秒提取上限,每天提取上限,使用時長等信息: (p ...
  • 2.1 你對軟體工程專業或者電腦科學與技術專業瞭解是怎樣? 軟體工程是一個比較熱門的行業,也是一門新行業,它是隨著電腦的產生應運而生的,現在我們走入信息化時代,此行業也會越來越熱門,我們也將成為此行業的中流砥柱。該行業所涉及的東西也十分廣泛,數學英語都是不可拉下的,既然如此,大家對此專業含金量也應 ...
  • 在原有的工程上,創建一個新的工程 創建service-zuul工程 其pom.xml文件如下: <?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="ht ...
  • 一、填空題 1.true、false 2.基本數據類型、引用數據類型 3.&、&&、|、|| 4.5 5.56 二、判斷題 1.× 2.√ 3.× continue語句只能用於迴圈語句,碰到continue語句就表示不執行後面的語句,直接轉到下一次迴圈的開始。 4.√ 5.× 三、選擇題 1.AD ...
  • [TOC] 序言:這一章我們學習字元串、列表、元組、字典 等常用存儲結構 1. 字元串 所謂 字元串 ,就是由零個或多個字元組成的有限序列 ,簡單來說:雙引號或者單引號中的數據,就是字元串 通過下麵代碼,我們來瞭解一下字元串的使用 輸出結果   str = "helloworld" | 方 ...
  • //程式崩潰規避 //Windows1、設置編譯器"Enable C++ Exceptions"為"/EHa",即"Yes with SEH Exceptions", 使得應用程式可以捕獲因自身引起的大部分系統異常,少部分不可迴避的系統異常依然 會導致程式崩潰退出;2、用"try{}catch(.. ...
  • 關於限流 常用的限流演算法有漏桶演算法和令牌桶演算法,guava的RateLimiter使用的是令牌桶演算法,也就是以固定的頻率向桶中放入令牌,例如一秒鐘10枚令牌,實際業務在每次響應請求之前都從桶中獲取令牌,只有取到令牌的請求才會被成功響應,獲取的方式有兩種:阻塞等待令牌或者取不到立即返回失敗,下圖來自網 ...
  • Task是.NET Framework3.0出現的,線程是基於線程池的,然後提供豐富的api,Thread方法很多很強大,但是太過強大,沒有限制。 DoSomethingLong方法如下: /// <summary> /// 一個比較耗時耗資源的私有方法 /// </summary> /// <pa ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...