毛毛的小窝 — 关注技术交流、让我们一起成长

导航

(原) 基础知识学习(一):委托和事件(delegate and event)

 

最简单的话题,拿来一起探讨一下,希望能引起一些新的思考。

1、委托的由来

    回调函数实际上是方法调用的指针,也称函数指针,.NET以委托的形式实现了函数指针的概念。简单说来,在编译时我们不知道第二个方法是什么,这个信息只能在运行时得到,所以需要把第二个方法作为参数传递给第一个方法。例如,Thread.Start(),方法必须带一个参数,改参数定义了要由线程调用的方法。另外,GUI编程主要是处理事件,发生事件时,运行库需要知道应执行哪个方法。

C语言中,我们可以这样使用函数指针:

void Max()

{

    ....

}

Thread.Start(Max);

    实际上,这是一种简单的方式,但这种直接的方法会导致一些问题,例如类型的安全性,在进行面向对象编程时,方法是很孤立的,在调用前,通常需要与类实例相关联。所以.NET Framework在语法上不允许使用这种直接的方法。如果要传递方法,就必须把方法的细节封装在一种新类型的对象中,即委托。委托是一种特殊的对象类型,不包含数据只包含方法的细节。

2、委托声明和使用

    类有两个不同的术语:“类”表示较广泛的定义,“对象”表示类的实例。但委托只有一个术语,在创建委托的实例时,所创建的委托的实例仍成为委托。

    使用委托要经过两个步骤,首先定义委托,告诉编译器委托代表哪种类型的方法,然后创建该委托的一个或多个实例。

    delegate int Max(int a, int b);  // 定义一个委托

    定义一个委托基本上是定义一个新类,委托的实现派生与基类System.MulticastDelegate的类,System.MulticastDelegate又派生于System.Delegate

    使用如下:

    Max max = new Max(CalMax);

    Console.Write(max(1,2).ToString());   

    int CalMax(int a, int b)

    {

        return a>b? a: b;

    }

3、示例(冒泡排序)

    要求:可以对所有的对象进行排序,不使用IComparer接口

1)定义委托,以应对两个对象的比较:

    delegate bool CompareOp(object lhs, object rhs);

2Sort方法签名:

    static public void Sort(object[] sortArray, CompareOp gtMethod)

3BubbleSorter

    public static class BubbleSorter

    {

        public static void Sort(object[] sortArray, CompareOp gtMethod)

        {

            ...

                ...

                if(gtMethod(sortArray[j],sortArray[i))

                {

                    ...

                }

        }

    }

4)对象的比较可以在类中进行

    public class Employee

    {

        ...

 

        public static bool RhsIsGreater(object lhs,object rhs)

        {

            return ((Employee)lhs.salary > (Employee)rhs.salary) ? true:false;

        }

    }

5)这样,我们可以在排序之前将委托传递给Sort

    CompareOp employeeCompareOp = new CompareOp(Employee.RhsIsGreater);

    BubbleSorter.Sort(employee,employeeCompareOp);

6)这中方式要求我们在排序之前要把两个对象比较的方法传递给Sort。这种方式对于对象的排序很方便,但是如果要求对int,double进行排序就比较麻烦,我们也要写一个比较两个int类型数据的方法,写一个比较两个double类型的方法。有没有更简单的呢,我们想到了泛型。

    public static class BubbleSorter

    {

        public static void Sort<T>(T[] sortArray)

        {

            for (int i = 0; i < sortArray.Length; i++)

                for (int j = i + 1; j < sortArray.Length; j++)

                    if (sortArray[i] < sortArray[j])

                    {

                        T temp = sortArray[i];

                        sortArray[i] = sortArray[j];

                        sortArray[j] = temp;

                    }

        }

    }

    但这是还有一个问题,运算符“<”无法应用于“T”和“T”类型的操作数,也就是说下面的语句是错误的:

    if(sortArray[i] < sortArray[j])

    怎么来解决这个问题呢??也就是怎么解决针对不同类型(intdouble等)的数据进行排序的通用方法??

4、多播委托

    要实现多播委托,委托必须返回void

多播委托支持++=--=等运算符,但应注意同一个委托调用方法链的顺序并未正式定义。

 

 

1、事件概念

    基于Windows的应用程序也是基于消息的。在MFC等库或VB等开发环境推出以前,开发人员必须处理Windows发送给应用程序的消息。.net把这些传送来的消息封装在事件中。如果需要响应某个消息,就应处理响应的事件。

    在开发基于对象的应用程序时,需要使用另一种对象通信方式。在一个对象中发生了有趣的事情时,就需要通知其他对象发生了什么变化。这里就用到了事件。委托就用作应用程序接收到消息时封装事件的方式。

2、探讨事件

    就单纯的事件(一般意思上)而言,事件的完整过程包括创建、引发、接收、取消。在.NET中,有两个形象的概念:事件发送器、事件接收器。另外必须注意,发送器并不知道接收器是谁。很明显,发送器的作用是引发事件,而接收器的作用是处理事件。由于发送器对接收器的一无所知,所以无法设置两者之间的引用类型,而是使用委托作为中介。发送器定义接收器要使用的委托,接收器将事件处理程序注册到事件中。

3、简单了解EventHandler委托

    EventHandler委托已经在.NET中定义,它位于System命名空间下。只有使用EventHandler委托,参数就应是objectEventArgs

    object:引发事件的对象。

    EventArgs:包含有关事件的其他有用信息的对象。

事件处理程序遵循“object_event.object”的命名约定,但不强求。

4、示例

内容:侦察兵监视敌军动向
namespace DelegateAndEvent
{
    
using System;
    
using System.Collections.Generic;

    
// 状态
    public enum Status
    
{
        Attack,  
// 攻击
        Suspend, // 暂停
        Retreat  // 撤退
    }


    
public class MobilizeEventArgs : EventArgs
    
{
        
private int _eventArgsNo;
        
private Status _status;
 
        
public int EventArgsNo
        
{
            
get return _eventArgsNo; }
            
set { _eventArgsNo = value; }
        }


        
public Status Status
        
{
            
get return _status; }
            
set { _status = value; }
        }

    }


    
// 敌军
    public class Enemy
    
{
        
private string _name;        // 名称
        private int _count;          // 数量
        private string _direction;   // 方向

        
public event EventHandler<MobilizeEventArgs> MobilizeEvent;

        
public Enemy(string name,int count, string direction)
        
{
            _name 
= name;
            _count 
= count;
            _direction 
= direction;
        }


        
public void Attack()
        
{
            MobilizeEventArgs e 
= new MobilizeEventArgs();
            e.Status 
= Status.Attack;

            OnMobilizeEvent(e);
        }


        
public void Retreat()
        
{
            MobilizeEventArgs e 
= new MobilizeEventArgs();
            e.Status 
= Status.Retreat;

            
// 溃败,返回老家
            this._direction = "南京";

            OnMobilizeEvent(e);
        }


        
private void OnMobilizeEvent(MobilizeEventArgs e)
        
{
            
if (MobilizeEvent != null)
                MobilizeEvent(
this, e);
        }


        
public string Name
        
{
            
get return _name; }
        }


        
public int Count
        
{
            
get return _count; }
        }


        
public string Direction
        
{
            
get return _direction; }
        }

    }


    
// 侦察员
    public class Scout
    
{
        
private Enemy _enemy;

        
public Scout(Enemy enemy)
        
{
            _enemy 
= enemy;
            _enemy.MobilizeEvent 
+= new EventHandler<MobilizeEventArgs>(MobilizeEvent);
        }


        
public void MobilizeEvent(object sender, MobilizeEventArgs e)
        
{
            e.EventArgsNo 
= 1;
            Console.WriteLine(
"No{0}: The enemy {1} are {2} to {3}.", e.EventArgsNo.ToString(), ((Enemy)sender).Name,
                e.Status.ToString(), ((Enemy)sender).Direction);
        }

    }

}
 

上面的示例采用了系统委托,我们也可以自定义委托,然后声明委托事件:

public delegate void ChangedEventHandler(object sender, MobilizeEventArgs e);

public event ChangedEventHandler MobilizeEvent;

客户端调用就很简单了,如下:

            // 初始化敌军

            Enemy enemy = new Enemy("汪精卫集团军", 10000, "北京");

            // 初始化侦察员监视汪精卫集团军

            Scout scout = new Scout(enemy);

            // 汪精卫集团军发起进攻

            enemy.Attack();

            // 汪精卫集团军出师不利,溃败

            enemy.Retreat();

小结

1、在学习委托的过程中,我们通过一个例子来描述了委托的用法,也提出了一些问题和思考。

2、事件很简单,我们都知道它的意思。但是事件也不简单,关键我们能够灵活运用。在学习事件的过程中,我们也可能会联想到观察者模式,想想做做可能更有意思。

 

参考文献

1C#高级编程

2MSDN

posted on 2008-01-04 15:04  mjgforever  阅读(482)  评论(0编辑  收藏  举报