編寫高質量代碼改善C#程序的157個建議[C#閉包的陷阱、委托、事件、事件模型]


前言

本文已更新至http://www.cnblogs.com/aehyok/p/3624579.html 。本文主要學習記錄以下內容:

  建議38、小心閉包中的陷阱

  建議39、了解委托的實質

  建議40、使用event關鍵字對委托施加保護

  建議41、實現標准的事件模型

建議38、小心閉包中的陷阱

  首先我們先來看一段代碼:

    class Program
    {
        static void Main(string[] args)
        {
            List<Action> list = new List<Action>();
            for (int i = 0; i < 5; i++)
            {
                Action t = () =>Console.WriteLine(i.ToString());
                list.Add(t);
            }
            foreach (Action t in list)
            {
                t();
            }
                Console.ReadLine();
        }
    }

你設想的結果或許是0,1,2,3,4

但沒想到執行后結果如下

通過IL可以查看代碼,組合后大致代碼如下:

    public class TempClass
    {
        public int i;
        public void TempFunc()
        {
            Console.WriteLine(i.ToString());
        }

    } 
    class Program
    {
        static void Main(string[] args)
        {
            List<Action> list = new List<Action>();
            TempClass tempClass = new TempClass();
            for (tempClass.i = 0; tempClass.i < 5; tempClass.i++)
            {
                Action t = tempClass.TempFunc;
                list.Add(t);
            }
            foreach (Action t in list)
            {
                t();
            }
                Console.ReadLine();
        }
    }

當然運行后結果還是5,5,5,5,5

其實這段代碼所演示的就是一個閉包對象。所謂的閉包對象,指的是上面這種情形中的TempClass對象,如果匿名方法(Lambda表達式)引用了某個局部變量,編譯器就會自動將該引用提升到該閉包對象中,即將for循環中的變量i修改成了引用閉包對象的公共變量i。這樣一來,即使代碼執行后離開了原局部變量i的作用域(如for循環),包含該閉包對象的作用域也還存在。

下面簡單修改一下之前的代碼

    class Program
    {

        static void Main(string[] args)
        {
            List<Action> list = new List<Action>();
            for (int i = 0; i < 5; i++)
            {
                int temp = i;
                Action t = () => Console.WriteLine(temp.ToString());
                list.Add(t);
            }
            foreach (Action t in list)
            {
                t();
            }
            Console.ReadLine();
        }
    }

執行結果如下:

建議39、了解委托的實質

 http://www.cnblogs.com/aehyok/archive/2013/03/22/2976356.html這里有我之前對委托的簡單的學習過程,雖然在工作中很少用,幾乎就沒用。不過還是拿來學習學習。

 理解委托需要把握兩個點:

1、委托是方法指針。

2、委托就是一個類。當對其進行實例化的時候,要將引用方法作為它構造函數的參數。

建議40、使用event關鍵字對委托施加保護

 http://www.cnblogs.com/aehyok/archive/2013/02/22/2922586.html 這也是對於事件的簡單理解學習。

建議41、實現標准的事件模型

我們應該知道微軟為事件模型設定的幾個規范:

1、委托類型的名稱以EventHandler結束。

2、委托原型返回值為void。

3、委托原型具有兩個參數:sender表示事件觸發者,e表示事件參數。

4、事件參數的名稱以EventArgs結束。

    public class FileUploadedEventArgs : EventArgs
    {
        public int FileProgress { get; set; }
    }

    public class FileUploader
    {
        public event EventHandler<FileUploadedEventArgs> FileUploaded;

        public void Upload()
        {
            FileUploadedEventArgs e = new FileUploadedEventArgs() { FileProgress=100 };
            while (e.FileProgress > 0)
            {
                ///傳輸代碼,省略
                e.FileProgress--;
                if (FileUploaded != null)
                {
                    FileUploaded(this, e);
                }
            }
        }
    }

最終進行調用的代碼如下:

    class Program
    {
        static void Main(string[] args)
        {
            FileUploader fileUploader = new FileUploader();
            fileUploader.FileUploaded += Progress;
            fileUploader.Upload();
            Console.ReadLine();
        }

        static void Progress(object sender,FileUploadedEventArgs e)
        {
            Console.WriteLine(e.FileProgress);
        }
    }

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM