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

原文:编写高质量代码改善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);
        }
    }

 

 

英语小贴士

1、Hello. This is United Airlines.——联合航空,您好。  I'd like to reconfirm my flight.——我想要再确认班机。

2、What's your name and flight number?——请说您的大名与班机号码?  

3、My name is Jerry Cheng, and the flight number is UA 003 for Los Angeles.——我的名字是杰瑞‧陈,班机号码是飞往洛杉机的联合航空003班机。

4、When is it?June 10th.——行程是那一天?6月10日。

5、I'd like to make sure of the time it leaves.——我想要确认班机时间没有改变。

6、I can't find your name. Really?——我找不到您的大名。真的?  May I have your name again?——请再告诉我一次您的大名?

7、I still can't find your name on the reservation list.——我仍然无法在订位名单中找到您的名字。

8、Anyway, we have seats for new bookings on this flight. No problem.——别担心,这班班机仍有空位提供新的订位者。

9、One economy class seat, is that right?——一个经济舱座位,对吗?  Now you have been booked.——没问题,您已完成订位。

10、Thanks a lot. What time do you start check-in?——谢谢。你们何时开始办理登机?

11、Two hours before departure time.——起飞前2小时。  You must check-in at least one hour before.——你必须在至少1小时前办理登机。

作者:aehyok

出处:http://www.cnblogs.com/aehyok/

感谢您的阅读,如果您对我的博客所讲述的内容有兴趣,那不妨点个推荐吧,谢谢支持:-O。

 

时间: 2024-09-19 12:30:07

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

编写高质量代码改善C#程序的157个建议[4-9]

原文:编写高质量代码改善C#程序的157个建议[4-9] 前言 本文首先亦同步到http://www.cnblogs.com/aehyok/p/3624579.html.本文主要来学习记录一下内容: 建议4.TryParse比Parse好 建议5.使用int?来确保值类型也可以为null 建议6.区别readonly和const的使用方法 建议7.将0值设为枚举的默认值 建议8.避免给枚举类型的元素提供显式的值 建议9.习惯重载运算符 建议4.TryParse比Parse好 如果注意观察,除st

编写高质量代码改善C#程序的157个建议[IEnumerable&lt;T&gt;和IQueryable&lt;T&gt;、LINQ避免迭代、LINQ替代迭代]

原文:编写高质量代码改善C#程序的157个建议[IEnumerable<T>和IQueryable<T>.LINQ避免迭代.LINQ替代迭代] 前言 本文已更新至http://www.cnblogs.com/aehyok/p/3624579.html .本文主要学习记录以下内容: 建议29.区别LINQ查询中的IEnumerable<T>和IQueryable<T> 建议30.使用LINQ取代集合中的比较器和迭代器 建议31.在LINQ查询中避免不必要的迭代

编写高质量代码改善C#程序的157个建议[为类型输出格式化字符串、实现浅拷贝和深拷贝、用dynamic来优化反射]

原文:编写高质量代码改善C#程序的157个建议[为类型输出格式化字符串.实现浅拷贝和深拷贝.用dynamic来优化反射] 前言 本文已更新至http://www.cnblogs.com/aehyok/p/3624579.html .本文主要学习记录以下内容: 建议13.为类型输出格式化字符串 建议14.正确实现浅拷贝和深拷贝 建议15.使用dynamic来简化反射实现 建议13.为类型输出格式化字符串   有两种方法可以为类型提供格式化的字符串输出. 一种是意识到类型会产生格式化字符串输出,于是

编写高质量代码改善C#程序的157个建议[为泛型指定初始值、使用委托声明、使用Lambda替代方法和匿名方法]

原文:编写高质量代码改善C#程序的157个建议[为泛型指定初始值.使用委托声明.使用Lambda替代方法和匿名方法] 前言 泛型并不是C#语言一开始就带有的特性,而是在FCL2.0之后实现的新功能.基于泛型,我们得以将类型参数化,以便更大范围地进行代码复用.同时,它减少了泛型类及泛型方法中的转型,确保了类型安全.委托本身是一种引用类型,它保存的也是托管堆中对象的引用,只不过这个引用比较特殊,它是对方法的引用.事件本身也是委托,它是委托组,C#中提供了关键字event来对事件进行特别区分.一旦我们

编写高质量代码改善C#程序的157个建议[匿名类型、Lambda、延迟求值和主动求值]

原文:编写高质量代码改善C#程序的157个建议[匿名类型.Lambda.延迟求值和主动求值] 前言 从.NET3.0开始,C#开始一直支持一个新特性:匿名类型.匿名类型由var.赋值运算符和一个非空初始值(或以new开头的初始化项)组成.匿名类型有如下基本特性: 1.既支持简单类型也支持复杂类型.简单类型必须是一个非空初始值,复杂类型则是一个以new开头的初始化项. 2.匿名类型的属性是只读的,没有属性设置器,它一旦倍初始化就不可更改. 3.如果两个匿名类型的属性值相同,那么就任务这两个匿名类型

编写高质量代码改善C#程序的157个建议[动态数组、循环遍历、对象集合初始化]

原文:编写高质量代码改善C#程序的157个建议[动态数组.循环遍历.对象集合初始化] 前言   软件开发过程中,不可避免会用到集合,C#中的集合表现为数组和若干集合类.不管是数组还是集合类,它们都有各自的优缺点.如何使用好集合是我们在开发过程中必须掌握的技巧.不要小看这些技巧,一旦在开发中使用了错误的集合或针对集合的方法,应用程序将会背离你的预想而运行. 本文已更新至http://www.cnblogs.com/aehyok/p/3624579.html .本文主要学习记录以下内容: 建议16.

编写高质量代码改善C#程序的157个建议[10-12]

原文:编写高质量代码改善C#程序的157个建议[10-12] 前言 本文已更新至http://www.cnblogs.com/aehyok/p/3624579.html .本文主要学习记录以下内容: 建议10.创建对象时需要考虑是否实现比较器 建议11.区别对待==和Equals 建议12.重写Equals时也要重写GetHashCode 建议10.创建对象时需要考虑是否实现比较器 有对象的地方就会存在比较,就像小时候每次拿着考卷回家,妈妈都会问你隔壁的那谁谁谁考了多少分呀.下面我们也来举个简单

编写高质量代码改善C#程序的157个建议[泛型集合、选择集合、集合的安全]

原文:编写高质量代码改善C#程序的157个建议[泛型集合.选择集合.集合的安全] 前言   软件开发过程中,不可避免会用到集合,C#中的集合表现为数组和若干集合类.不管是数组还是集合类,它们都有各自的优缺点.如何使用好集合是我们在开发过程中必须掌握的技巧.不要小看这些技巧,一旦在开发中使用了错误的集合或针对集合的方法,应用程序将会背离你的预想而运行. 本文已更新至http://www.cnblogs.com/aehyok/p/3624579.html .本文主要学习记录以下内容: 建议20.使用

编写高质量代码改善C#程序的157个建议[协变和逆变]

原文:编写高质量代码改善C#程序的157个建议[协变和逆变] 前言 本文已更新至http://www.cnblogs.com/aehyok/p/3624579.html .本文主要学习记录以下内容: 建议42.使用泛型参数兼容泛型接口的不可变性 建议43.让接口中的泛型参数支持协变 建议44.理解委托中的协变 建议45.为泛型类型参数指定协变 建议42.使用泛型参数兼容泛型接口的不可变性 让返回值类型返回比声明的类型派生程度更大的类型,就是"协变".协变不是一种新出现的技术,在以往的编