zoukankan      html  css  js  c++  java
  • 编写高质量代码改善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);
            }
        }
  • 相关阅读:
    React-精华版
    国内优秀npm镜像推荐及使用
    GitHub 配置指南
    Nodejs之WebSocket
    js验证连续两位数字递增或递减和连续三位数字相同
    JS魔法堂:LINK元素深入详解
    phpstorm将多个int数字拼接成字符串
    php中使用curl来post一段json数据
    MySQL索引使用:字段为varchar类型时,条件要使用''包起来
    MySQL中enum类型数据,要传入字符串
  • 原文地址:https://www.cnblogs.com/aehyok/p/3730417.html
Copyright © 2011-2022 走看看