编写高质量代码改善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

但没想到执行后结果如下

编写高质量代码改善C#程序的157个建议[C#闭包的陷阱、委托、事件、事件模型]

通过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();

        }

    }

执行结果如下:

编写高质量代码改善C#程序的157个建议[C#闭包的陷阱、委托、事件、事件模型]

建议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);

        }

    }

 

 

 

你可能感兴趣的:(高质量代码)