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