• 技术讲座:.NET委托、事件及应用兼谈软件项目开发


    1. NET委托及应用


    1.1 .NET委托概念
    OOP中具有相同属性的对象抽象后成为类型(class)。那么,具有相同属性的函数或方法(也称具有相同的函数签名):

    返回类型相同
    参数类型、参数顺序及参数个数相同
    抽象后又是什么概念?例如,1到n之间每个数的平方后求和函数int SquareSum(int n)和立方后求和函数int CubeSum(int n),它们具有相同的函数签名:返回类型int、参数只有一个且是int类型。static private int SquareSum(int n)
    {
        int m = 0;
        for (int k = 1; k <= n; k++)
        {
            m += k * k;
        }
        return m;
    }static private int CubeSum(int n)
    {
        int m = 0;
        for (int k = 1; k <= n; k++)
        {
            m += k * k * k;
        }
        return m;
    }
    这些相同属性的函数抽象,就是.NET提出的一个新的类型概念——委托,关键字为delegate。

    1.2 .NET委托声明及特点
    与C/C++/C#的函数声明相同,声名一个委托需要有:委托名、返回类型、参数及类型。例如,声明前面定义的两个函数的委托PowerSum如下:
    public delegate int PowerSum(int n);特别地,一类通用的事件处理委托EventHandler声明如下:
    public delegate void EventHandler(object sender, EventArgs e)显然,与类定义不同,委托声名不需要定义成员,它只起一个表示作用(delegate就是代表的意思)。此外,delegate也是类,其基类是MulticastDelegate,再上层类是Delegate,顶层类是object。

    1.3 .NET委托应用描述
    Microsoft .NET Framework 通过委托向外提供一种函数回调机制。——《框架设计(第2版)》Jeffrey Richeter
    是一种类型安全的方法引用,可以把它看成一个类型安全的C函数指针。——《.NET组件编程》Juval Lowy
    要把方法传送给其它方法时需要委托。与C函数指针不同,.NET委托是类型安全的。——《C#高级编程》Christian Nagel
    从上面名著的描述可以看出,.NET委托的主要用途是三个:1)函数回调;2)传递方法;3)事件机制。

    1.4 .NET委托举例1:传递方法
    委托作为方法传递时,有两种方式。第一种,直接传递方法,这种方式称为委托推断;第二种,创建委托对象后传递,这种方式是常规方式。应用前面定义的委托,现定义一个调用方法的函数:int GetPowerSum(PowerSum ps)如下,该函数用于计算1到10的指数和。
    static private int GetPowerSum(PowerSum ps)
    {
        return ps(10);
    }采用委托推断方式调用代码如下:
    int p2 = GetPowerSum(SquareSum);
    int p3 = GetPowerSum(CubeSum);采用创建委托对象方式调用代码如下:
    PowerSum ps2 = new PowerSum(SquareSum);
    PowerSum ps3 = new PowerSum(CubeSum);

    p2 = ps2(10);
    p3 = ps3(10);

    1.5 .NET委托举例2:函数回调
    最常见的回调应用之一,是计时器到点时调用的函数。涉及到的类型如下(.NET有三个计时器类型,这个是线程名称空间System.Threading里的Timer):
    public sealed Timer(TimerCallBack callback, object state, int dueTime, int period);
    public delegate void TimeCallBack(object state);
    Timer类型中,callback是一个委托TimerCallBack的对象;state是调用时的状态参数,可以灵活应用;dueTime是计时器开始计时的等待时间;period是计时周期,每完成一个周期就调用方法callback
    回调函数CalllBack的委托定义表明,计时器类Timer到点时回调的函数不能有返回类型,但必须有一个参数object型的参数。注意,此处委托的所谓逆变不能用了

    现定义一个到点回调函数,即到点就输出字符串信息如下:
    static private void TimeClick(object state)
    {
        Console.WriteLine("time click 500ms");
    }那么500ms报时的计时器应用代码如下:
    System.Threading.TimerCallback callBack = new System.Threading.TimerCallback(TimeClick);
    System.Threading.Timer timer = new System.Threading.Timer(callBack, null, 0, 500);由于回调函数比较简单,可以使用匿名委托,代码如下
    System.Threading.TimerCallback callBack = new System.Threading.TimerCallback
    (
        delegate(object state)
        {
            Console.WriteLine("time click 500ms");
        }
    );

    System.Threading.timer = new System.Threading.Timer(callBack, null, 0, 500);

    2 .NET事件及应用


    2.1 .NET事件概念
    一个如何获得另一个对象发生某个事件的通知?VB和C#中常用的方法如下

    VB按钮(Command)点击事件:Sub Command1_Click()
    C#按钮(Button)点击事件:void button1_Click(object sender, EventArgs e)
    这表明,事件是一种信号机制,对象在发生某种活动时自动发出通知,是对象定义的外发消息接口。其它对象若对事件感兴趣,则为该事件注册一个事件处理程序。事件发生时,所有注册在该事件上的处理程序都会被调用。

    发布事件的对象称为发布者(publisher)或事件源,发布事件也称为激发(fire)事件。关注事件的对象称为事件接收器(sinker)或订阅者(subscriber),订阅事件也称为注册事件方法。发布者调用订阅者的注册方法。.NET事件模型建立在委托机制之上,支持事件定义、发布、订阅、和拆除。

    2.2 设计.NET事件5个步骤
    定义参数类型:从类型EventArgs派生出满足要求的事件参数类
    定义事件处理者委托:与第1)步相关,该步一般被泛型委托取代了
    定义事件成员:在自定义类中,由事件处理者委托定义一个或多个事件成员
    激发事件:在自定义类的引发事件方法中,通知所有事件订阅者
    订阅事件:其它对象注册事件处理程序
    需要指出,上述第3、4、5步必须存在,第1、2步可适当省略:

    第2步可省。如果采用标准事件处理者委托类型:void EventHandler(object sender, EventArgs e),那么只需要第1步给出事件参数,然后使用泛型委托:EventHandler<T>即可定义类的事件成员了。其中,T就是事件参数类型
    如果没有自定义事件参数,可以省略第1、2步,直接用EventHandler定义类的事件成员

    2.3 事件设计举例
    编写一个统计按键次数的键盘侦听类TKeyListen
    TKeyListen可以发布侦听到的击键次数,并检查返回参数值
    注册事件的对象可以终止侦听循环
    第1步:定义事件参数类
    public class KeyEventArgs : EventArgs
    {
        private int m_KeyCount;
        private bool m_Stop = false;

        public KeyEventArgs(int keyCount) // 发布事件时给出按键计数值
        {
            m_KeyCount = keyCount;
        }

        public int KeyCount
        {
            get { return m_KeyCount; }
        }

        public bool Stop
        {
            get { return m_Stop; }
            set { m_Stop = value; }  // 事件订阅者可以修改
        }
    }
    第2步:声明事件处理者委托
    public delegate void KeyEventHandler(object sender, KeyEventArgs e);实际使用时,除非上述委托有其它用途,一般使用泛性委托EventHandler<T>取代,其中T就是事件参数类型。

    第3、4步:定义类事件成员、激发(发布)事件
    <!--[if !ppt]-->public class TKeyListen
    {
    //    public event KeyEventHandler KeyPress;  // 第3步:定义事件成员
      public event EventHandler<KeyEventArgs> KeyPress;  // 第3步:泛型委托实现

        public void Listen()
        {
            Console.WriteLine("Please press key.");
            int keyCount = 0;

            while (true) // 使用循环监听击键动作
            {
                ConsoleKeyInfo key = Console.ReadKey();
                keyCount++;

                if (KeyPress != null)  // 如果存在订阅者,即:委托链非空
                {
                    KeyEventArgs e = new KeyEventArgs(keyCount);
                    KeyPress(this, e);  // 第4步:激发事件,通知所有订阅者
                   
                    if (e.Stop)  // 判断事件返回参数
                   {
                        break;
                    }
                }
            }
        }
    }上述代码包含了事件实现的第3、4步。其中循环代码while(true)包含了发布(激发)事件,特别说明如下:

    必须判断委托链(订阅者链)是否空,即是否存在事件的订阅者:if (KeyPress != null)。如果没有事件订阅者,直接发布事件KeyPress(this,e),系统将抛出异常“未将对象引用设置到对象实例上”
    激发或发布事件时,第一个参数是对象自己(this):KeyPress(this, e)

    KeyPress(this,e)实际执行过程:遍历事件订阅者链,执行每个订阅事件方法,这些方法具有与KeyPress相同的委托类型
    可以判断事件返回参数,即订阅者可以修改参数e.Stop,发布者检测该参数。如果有多个订阅者,上述代码只获得最后一个订阅事件处理方法给定的参数。如果要判断每个订阅方法的参数,必须使用委托的GetInvocationList()方法,逐个获得返回参数。
    第5步:订阅事件
    static void Main(string[] args)
    {
        TKeyListen demo = new TKeyListen();
        demo.KeyPress += CountKey;  // 订阅事件,使用委托推断方式
        demo.Listen();
    }

    static void CountKey(object sender, KeyEventArgs e) // 事件处理方法
    {
        Console.WriteLine("Press count: " + e.KeyCount);
        if (e.KeyCount == 5)  e.Stop = true; // 5次后停止
    }注意,上述代码中,事件处理方法CountKey必须与事件处理者委托或泛型委托一致。此外,需要说明如下:

    订阅事件操作符为:+=,移除订阅操作为-=
    建立委托对象订阅方式:demo.KeyPress += new KeyEventHandler(CountKey);

    可以多次订阅,从而产生事件链:demo.KeyPress += delegate() {…}
    小结:委托的主要用途是方法调用、函数回调和事件,而事件重要用于外发消息。


    3 软件项目开发浅谈
    3.1 开发文档
    即使是小型软件项目,也至少需要包括如下开发文档:

    技术文档:需求分析、系统运行环境、系统运行配置、库表设计、算法描述等
    测试文档:单元/集成测试、验收测试

    3.2 后期维护
    长期维护准备:维护期限一般体现在合同中,一般3~5年
    代码与文档维护:增加维护文档,记录所有的维护情况

    3.3 技术积累
    技术侧重:工具与语言、B/S与C/S模式
    最新技术:例如:RIA中的SilverLight与Flex
    行业知识:应用领域的专业知识

    上面主要是小型、个人最多不过3人的软件项目,不涉及到大型的、公司里运作的项目开发方式。

    文章出处:飞诺网(www.firnow.com):http://dev.firnow.com/course/4_webprogram/asp.net/asp_netshl/2008102/147320_2.html

  • 相关阅读:
    js数组和数组去重的几种简单的方法
    nodejs项目的model操作mongo
    canvas画布
    bson
    神奇的东西
    sql与nosql
    mong大牛的blog
    mongo 增删改查
    Mongo配置基础
    session放数据库里解决丢失的问题
  • 原文地址:https://www.cnblogs.com/zcy_soft/p/1842019.html
Copyright © 2020-2023  润新知