事件,委托举例子 。在C#中使用代理的方式触发事件

事件,委托举例子 

首先,我们来看看在非委托的情况下比较常见的一种设计方式(当然这不是唯一的方式,也不是最好的方式,但是很常见):
using System;
namespace CSharpConsole
{
    public class 场景
    {
[STAThread]
public static void Main(string[] args)
{
Console.WriteLine("场景开始了.");
// 生成主管类的对象实例 小王
主管 小王 = new 主管();
// 生成员工类的对象实例 小张,指定他的主管
员工 小张 = new 员工(小王);

Console.WriteLine("该员工本有的薪水:" + 小张.薪水.ToString());

// 员工开始玩游戏
小张.玩游戏();
Console.WriteLine("现在该员工还剩下:" +小张.薪水.ToString());

Console.WriteLine("场景结束");
Console.ReadLine();
}
    }

    // 负责扣钱的人----主管
    public class 主管
    {
public 主管()
{
Console.WriteLine("生成主管");
}
public void 扣薪水(员工 employee)
{
Console.WriteLine("主管:好小子,上班时间胆敢玩游戏");
Console.WriteLine("主管:看看你小子有多少薪水");

Console.WriteLine("开始扣薪水...");
System.Threading.Thread.Sleep(1000);
employee.薪水 = employee.薪水 - 500;
Console.WriteLine("扣薪水执行完毕.");     
}
    }
    // 如果玩游戏,则会引发事件
    public class 员工
    {
// 保存员工的薪水
private int m_Money;
// 保存该员工的主管
private 主管 m_Manager;
public 员工(主管 manager)
{
Console.WriteLine("生成员工.");
m_Manager = manager; // 通过构造函数,初始化员工的主管。
m_Money = 1000; // 通过构造函数,初始化员工的薪水。
}
public int 薪水 // 此属性可以操作员工的薪水 。
{
get
{
    return m_Money;
}
set
{
    m_Money = value;
}
}
public void 玩游戏()
{
Console.WriteLine("员工开始玩游戏了..");
Console.WriteLine("员工:CS真好玩,哈哈哈! 我玩...");
System.Threading.Thread.Sleep(1000);

m_Manager. 扣薪水(this);
}
    }
}
这种方法所带来的问题: 员工类和主管类的耦合性太高
1、   在客户程序里必须先创建了主管类之后才能生成员工类,如果在不需要主管类对象而只需员工类对象的地方,为了创建所需的员工类对象实例,你也不得不去先创建一个主管类的对象实例;
2、   如果场景剧本(即客户程序需求)发生了变化
(1)、现在要让一个新的角色(一个新的类),如保安,来代替主管,负责在员工玩游戏时扣员工薪水,那么我们不得不去修改员工类,或许还需要修改主管类;
(2)、如果场景剧本增加新的需求,要求员工在玩游戏后,不但要扣薪水,还要在绩效上扣分,那么我们也不得不修改员工类。
(二)、利用委托的实现:
下面有个例子:在C# 控制台应用程序编辑运行成功:
using System;
namespace CSharpConsole
{
    // 定义委托
    public delegate void PlayGameHandler(object sender,System.EventArgs e);
    // 负责扣钱的人----主管
    public class 主管
    {
public 主管()
{
Console.WriteLine("生成主管");
}
public void 扣薪水(object sender,EventArgs e)
{
Console.WriteLine("主管:好小子,上班时间胆敢玩游戏");
Console.WriteLine("主管:看看你小子有多少薪水");
    
员工 employee = (员工)sender;
    
Console.WriteLine("开始扣薪水...");
System.Threading.Thread.Sleep(1000);
employee.薪水 = employee.薪水 - 500;
Console.WriteLine("扣薪水执行完毕.");     
}
    }
    // 如果玩游戏,则会引发事件
    public class 员工
    {
// 先定义一个事件,这个事件表示员工在玩游戏。
public event PlayGameHandler PlayGame;
// 保存员工薪水的变量
private int m_Money;
public 员工()
{
Console.WriteLine("生成员工.");
m_Money = 1000; // 构造函数,初始化员工的薪水。
}
public int 薪水 // 此属性可以操作员工的薪水 。
{
get
{
    return m_Money;
}
set
{
    m_Money = value;
}
}
public void 玩游戏()
{
Console.WriteLine("员工开始玩游戏了..");
Console.WriteLine("员工:CS真好玩,哈哈哈! 我玩...");
System.Threading.Thread.Sleep(1000);
System.EventArgs e = new EventArgs();
OnPlayGame(e);
}
protected virtual void OnPlayGame(EventArgs e)
{
if(PlayGame != null)
{
    PlayGame(this,e);
}
}
    }
    public class 场景
    {
[STAThread]
public static void Main(string[] args)
{
Console.WriteLine("场景开始了.");
// 生成主管类的对象实例 小王
主管 小王 = new 主管();
// 生成员工类的对象实例 小张
员工 小张 = new 员工();
// 设下委托,指定监视
小张.PlayGame += new PlayGameHandler(小王. 扣薪水);

Console.WriteLine("该员工本有的薪水:" + 小张.薪水.ToString());

// 员工开始玩游戏
小张.玩游戏();
Console.WriteLine("现在该员工还剩下:" +小张.薪水.ToString());

Console.WriteLine("场景结束");
Console.ReadLine();
}
    }
}
对于前面提出的问题:
1、   解耦了主管类和员工类之间的必然联系,可以单独创建员工类对象实例,而不用管是否有主管类对象实例的存在;
2、   在客户程序需求变化时:
(1)、我们只需修改客户程序,即上面例子里的class 场景,将委托改为如下:
保安 小李 = new 保安();
小张.PlayGame += new PlayGameHandler(小李. 扣薪水);
即可实现由保安来负责扣薪水的需求变化,而不用动员工类。
(2)、我们只需修改客户程序,即上面例子里的class 场景,添加一个如下的委托:
    小张.PlayGame += new PlayGameHandler(某某. 扣绩效分);
这个“某某”可以是主管,也可以是其他新的角色(新的类),只需要在“某某”对应的类里定义扣绩效分的动作即可,而不用动员工类。
四、总结:
当然,不使用委托和事件我们仍然可以设计出解耦的类,然而却会增加很多的类、接口以及关联等等,增加了代码量和程序的逻辑复杂性,而在.net里利用委托和事件我们只需少的多的代码来实现。
委托和事件的使用有如下几个要素:
1、激发事件的对象-----就是员工小张
2、处理对象事件的对象-----就是主管小王
3、定义委托,就是你让主管小王监视员工小张。
如果这三个要素都满足的话,则你就写出了一个完整事件的处理。

================================================================================

在C#中使用代理的方式触发事件   

事件(event)是一个非常重要的概念,我们的程序时刻都在触发和接收着各种事件:鼠标点击事件,键盘事件,以及处理操作系统的各种事件。所谓事件就是由某个对象发出的消息。比如用户按下了某个按钮,某个文件发生了改变,socket上有数据到达。触发事件的对象称作发送者(sender),捕获事件并且做出响应的对象称作接收者(receiver),一个事件可以存在多个接受者。 

在异步机制中,事件是线程之间进行通信的一个非常常用的方式。比如:用户在界面上按下一个按钮,执行某项耗时的任务。程序此时启动一个线程来处理这个任务,用户界面上显示一个进度条指示用户任务执行的状态。这个功能就可以使用事件来进行处理。可以将处理任务的类作为消息的发送者,任务开始时,发出“TaskStart”事件,任务进行中的不同时刻发出“TaskDoing”事件,并且携带参数说明任务进行的比例,任务结束的时候发出“TaskDone”事件,在画面中接收并且处理这些事件。这样实现了功能,并且界面和后台执行任务的模块耦合程度也是最低的。 

具体说C#语言,事件的实现依赖于“代理”(delegate)的概念,先了解一下代理。 

代理(delegate) 

delegate是C#中的一种类型,它实际上是一个能够持有对某个方法的引用的类。与其它的类不同,delegate类能够拥有一个签名(signature),并且它只能持有与它的签名相匹配的方法的引用。它所实现的功能与C/C++中的函数指针十分相似。它允许你传递一个类A的方法m给另一个类B的对象,使得类B的对象能够调用这个方法m。但与函数指针相比,delegate有许多函数指针不具备的优点。首先,函数指针只能指向静态函数,而delegate既可以引用静态函数,又可以引用非静态成员函数。在引用非静态成员函数时,delegate不但保存了对此函数入口指针的引用,而且还保存了调用此函数的类实例的引用。其次,与函数指针相比,delegate是面向对象、类型安全、可靠的受控(managed)对象。也就是说,runtime能够保证delegate指向一个有效的方法,你无须担心delegate会指向无效地址或者越界地址。 

实现一个delegate是很简单的,通过以下3个步骤即可实现一个delegate: 
1. 声明一个delegate对象,它应当与你想要传递的方法具有相同的参数和返回值类型。 
2. 创建delegate对象,并将你想要传递的函数作为参数传入。 
3. 在要实现异步调用的地方,通过上一步创建的对象来调用方法。 
下面是一个简单的例子: 

public class MyDelegateTest 

// 步骤1,声明delegate对象 
public delegate void MyDelegate(string name); 
// 这是我们欲传递的方法,它与MyDelegate具有相同的参数和返回值类型 
public static void MyDelegateFunc(string name) 

Console.WriteLine("Hello, {0}", name); 

public static void Main () 

// 步骤2,创建delegate对象 
MyDelegate md = new MyDelegate(MyDelegateTest.MyDelegateFunc); 
// 步骤3,调用delegate 
md("sam1111"); 



输出结果是:Hello, sam1111 

下面我们来看看事件是如何处理的:

C#中处理事件

C#中的事件处理实际上是一种具有特殊签名的delegate,象下面这个样子:

public delegate void MyEventHandler(object sender, MyEventArgs e);

其中的两个参数,sender代表事件发送者,e是事件参数类。MyEventArgs类用来包含与事件相关的数据,所有的事件参数类都必须从System.EventArgs类派生。当然,如果你的事件不含参数,那么可以直接用System.EventArgs类作为参数。

就是这么简单,结合delegate的实现,我们可以将自定义事件的实现归结为以下几步:

1.           定义delegate对象类型,它有两个参数,第一个参数是事件发送者对象,第二个参数是事件参数类对象。

2.           定义事件参数类,此类应当从System.EventArgs类派生。如果事件不带参数,这一步可以省略。

3.           定义事件处理方法,它应当与delegate对象具有相同的参数和返回值类型。

4.           用event关键字定义事件对象,它同时也是一个delegate对象。

5.           用+=操作符添加事件到事件队列中(-=操作符能够将事件从队列中删除)。

6.           在需要触发事件的地方用调用delegate的方式写事件触发方法。一般来说,此方法应为protected访问限制,既不能以public方式调用,但可以被子类继承。名字是OnEventName。

7.           在适当的地方调用事件触发方法触发事件。

下面是一个简单的例子:
using System;

public class EventTest
{
      // 步骤1,定义delegate对象
     public delegate void MyEventHandler(object sender, System.EventArgs e);

     // 步骤2省略
     public class MyEventCls
     {
           // 步骤3,定义事件处理方法,它与delegate对象具有相同的参数和返回值类// 型
            public void MyEventFunc(object sender, System.EventArgs e)
            {
                   Console.WriteLine("My event is ok!");
            }
     }

     // 步骤4,用event关键字定义事件对象
     private event MyEventHandler myevent;
     private MyEventCls myecls;
     public EventTest()
     {
            myecls = new MyEventCls();
            // 步骤5,用+=操作符将事件添加到队列中
            this.myevent += new MyEventHandler(myecls.MyEventFunc);
     }
     // 步骤6,以调用delegate的方式写事件触发函数
     protected void OnMyEvent(System.EventArgs e)
     {
            if(myevent != null)
               myevent(this, e);
     }
     public void RaiseEvent()
     {
            EventArgs e = new EventArgs();
            // 步骤7,触发事件
            OnMyEvent(e);
     }

     public static void Main()
     {
            EventTest et = new EventTest();
            Console.Write("Please input 'a':");
            string s = Console.ReadLine();
            if(s == "a")
            {
                   et.RaiseEvent();
            }
            else
            {
                   Console.WriteLine("Error");
            }
     }
}

猜你喜欢

转载自blog.csdn.net/hljqfl/article/details/86316064