国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

溫故知新---重讀C#InDepth(二)

2019-11-17 03:01:04
字體:
來源:轉載
供稿:網友

溫故知新---重讀C#InDepth(二)

一本好書,或是一本比較有深度的書,就是每次研讀的時候都會有新的發現。

好吧,我承認每次讀的時候都有泛泛而過的嫌疑~~

這幾年一直專注于C#客戶端的開發,逐步從迷迷糊糊,到一知半解,再到自以為是,最后沉下心重新審視。也許這也是一種進步一種自我學習的過程。

前面啰嗦了這么多,希望大家也能不那么浮躁的“深入理解”C#這門語言的每個知識點。本文總結書本中的知識,在結合實際應用場合進行概述,如果有不正確的地方,還請不吝指教。

文章中的內容比較淺顯,請高手略過此文。

4. 程序閉包

程序閉包的問題是由于程序對某些變量進行了預判和處理(個人理解,若有誤或不足請指正)使得某些變量理應作為值類型卻變為了引用類型導致數據異常。

當然,大多數情況下,我們是不會遇到這樣的問題,但在某些情況下,我們不得不注意并分析問題的根本原因,BUG永遠不是隨機的。

下面通過幾個例子逐步來理解閉包的概念:

例4.1

        PRivate void Button1_Click(object sender, RoutedEventArgs e)        {            int outerVariableCaptured = 5; // 外部變量(被捕獲)            int outerVariableUnCaptured = 50; // 外部變量(未捕獲)            if (DateTime.Now.Hour <= 24)            {                int normalLocalVariable = 1; // 普通方法的局部變量,不是外部變量,因為在其作用域內無匿名方法。                this.Txb_Msg.Text += string.Format("普通方法的局部變量 = {0}", normalLocalVariable) + System.Environment.NewLine;            }            Action x = new Action(() =>            {                int anonLocal = 2; // 匿名方法的局部變量                this.Txb_Msg.Text += string.Format("匿名方法的局部變量 = {0}", anonLocal) + System.Environment.NewLine;                this.Txb_Msg.Text += string.Format("匿名方法中被捕獲的外部變量 = {0}", outerVariableCaptured)                    + System.Environment.NewLine; // 匿名方法中調用了作用域外的變量,所以變量變為被捕獲的外部變量            });            this.Txb_Msg.Text += string.Format("普通方法的未捕獲的外部變量 = {0}", outerVariableUnCaptured) + System.Environment.NewLine;            x();        }
輸出結果:普通方法的局部變量 = 1普通方法的未捕獲的外部變量 = 50匿名方法的局部變量 = 2匿名方法中被捕獲的外部變量 = 5

4.1中是讓大家了解外部變量,局部變量,捕獲等相關概念。其中最重要的是被捕獲的外部變量。

例4.2

        private void Button3_Click(object sender, RoutedEventArgs e)        {            // 證明被捕捉的局部變量聲明周期被延長了。            OnCreateDelegate += MainWindow_OnCreateDelegate;            this.Dispatcher.Invoke(OnCreateDelegate(this)); // 此處Invoke容易引起歧義:原因在于Invoke事件之后返回的還是一個事件。            // Counter是值類型,逃脫其作用域時棧上數據會被回收,真實的情況是這樣嗎?            // 從另一個側面也說明了,值類型是在棧上還是堆上,依賴于創建對象的類型。        }
        private Delegate MainWindow_OnCreateDelegate(object sender)        {            var frm = sender as MainWindow;            int Counter = 1;            var a = new Action(() =>            {                frm.Txb_Msg.Text += string.Format("委托內部的變量值 = {0}", Counter) + System.Environment.NewLine;                Counter++;            });            a();            return a;        }
輸出:委托內部的變量值 = 1委托內部的變量值 = 2

這個例子要說明的是Counter其值類型原本的生存周期應該在MainWindow_OnCreateDelegate(object sender)方法中,可是偏偏卻逃離了方法的作用域,這就是我們所說的值類型是在堆上Or棧上完全取決于其初始化的位置是在棧上還是在堆上。

例4.3

        private void Button4_Click(object sender, RoutedEventArgs e)        {            // 更復雜的一些情況            var methods = new Action[2];            int outside = 10; // 實例化變量一次            for (int i = 0; i < 2; i++)            {                int inside = 100; // 實例化變量多次                methods[i] = new Action(() =>                {                    this.Txb_Msg.Text += string.Format("Inside Value = {0}; Outside Value = {1} ", inside, outside) + System.Environment.NewLine;                    inside++;                    outside++; // 匿名方法捕獲的變量                });            }            methods[0].Invoke();            methods[0].Invoke();            methods[0].Invoke();            methods[1].Invoke();        }
輸出結果:            /***************Outside變量內存共享*************             * Inside Value = 100; Outside Value = 10              * Inside Value = 101; Outside Value = 11              * Inside Value = 102; Outside Value = 12              * Inside Value = 100; Outside Value = 13              * *******************************************/

這個例子就得好好想想了,outside和inside的值到底會是什么?為什么會這樣?原因在于inside在For循環的內部初始化了多次,也就是說For循環幾次,就有幾個獨立的inside對象,雖說它是值類型。

例4.4

這個例子摘自《編寫高質量代碼:改善C#程序的157個建議》

        private void Button5_Click(object sender, RoutedEventArgs e)        {            // 閉包陷阱             var methods = new Action[2];            for (int i = 0; i < 2; i++)            {                int inside = i; // 實例化變量多次                methods[i] = new Action(() =>                {                    this.Txb_Msg.Text += string.Format("Inside Value = {0}; Index Value = {1} ", inside, i) + System.Environment.NewLine;                });            }            methods[0].Invoke();            methods[1].Invoke();            /*****************閉包陷阱*********************             * 當使用i的值時,i就是前面說的共享變量(捕獲的外部變量),所以總是輸出i的最大值。             * 當使用Inside值時,Inside就是內部變量,每次創建對象都重新生成,所以此處inside的值是遞增,即緩存下i的值。             * 對于IL,其創建了Tempclass.i來代替i,導致了i值共享。             * *******************************************/         }
輸出結果:Inside Value = 0; Index Value = 2 Inside Value = 1; Index Value = 2 

其實如果用ILDasm來看的話,針對i這個對象,IL生成了一個DisplayClass(就是一個名字而已)這樣一個類,最總導致了,i變為引用類型,數據異常。

持續更新:示例代碼下載


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 澄迈县| 乐业县| 静乐县| 永安市| 正定县| 永顺县| 定远县| 宜兰县| 布尔津县| 新民市| 赣榆县| 博兴县| 漳平市| 呼和浩特市| 吕梁市| 新余市| 沙雅县| 嘉禾县| 靖边县| 都昌县| 定安县| 华阴市| 明溪县| 胶州市| 绩溪县| 德安县| 张家港市| 寿光市| 柏乡县| 安西县| 龙川县| 什邡市| 瑞金市| 和静县| 昭觉县| 江源县| 南皮县| 锦州市| 克什克腾旗| 建宁县| 凤山市|