delegate和event的区别

一. delegate

    C#代理实际上类似于C++中的函数指针,因为C#中不存在指针,所以用代理可以完成一些原来在C++中用函数指针完成的操作,例如传递一个类A的方法m给另一个类B的对象,使得类B的对象能够调用这个方法m。但与函数指针相比,delegate有许多函数指针不具备的优点。首先,函数指针只能指向静态函数,而delegate既可以引用静态函数,又可以引用非静态成员函数。在引用非静态成员函数时,delegate不但保存了对此函数入口指针的引用,而且还保存了调用此函数的类实例的引用。其次,与函数指针相比,delegate是面向对象、类型安全、可靠的受控(managed)对象。也就是说,runtime能够保证delegate指向一个有效的方法,你无须担心delegate会指向无效地址或者越界地址。

    通过以下3个步骤即可实现一个delegate

1 声明一个delegate对象,它应当与你想要传递的方法具有相同的参数和返回值类型。

    声明一个代理的例子:

    public delegate int MyDelegate(string message);

2 创建delegate对象,并将你想要传递的函数作为参数传入。

     创建代理对象的方法:

    1). MyDelegate myDelegate = new MyDelegate(实例名.方法名);

    2). MyDelegate myDelegate = new MyDelegate(类名.方法名);

注:如果需要代理的方法是一个static静态方法的话,采用第2种方式,否则采用第1种方式。

3 在要实现异步调用的地方,通过上一步创建的对象来调用方法。

    可以直接使用代理调用代理所指向的方法:

    myDelegate(向方法传递的参数);

    下面是一些需要注意的事情:

“代理”(delegate)(代表、委托):“代理”是类型安全的并且完全面向对象的。

1)在C#中,所有的代理都是从System.Delegate类派生的(delegateSystem.Delegate的别名)。

2)代理隐含具有sealed属性,即不能用来派生新的类型。

3)代理最大的作用就是为类的事件绑定事件处理程序。

4)在通过代理调用函数前,必须先检查代理是否为空(null),若非空,才能调用函数。(5)在代理实例中可以封装静态的方法也可以封装实例方法。

6)在创建代理实例时,需要传递将要映射的方法或其他代理实例以指明代理将要封装的函数原型(.NET中称为方法签名:signature)。注意,如果映射的是静态方法,传递的参数应该是类名.方法名,如果映射的是实例方法,传递的参数应该是实例名.方法名。

7)只有当两个代理实例所映射的方法以及该方法所属的对象都相同时,才认为它们是想等的(从函数地址考虑)。

8)多个代理实例可以形成一个代理链,System.Delegate中的定义了用来维护代理链的静态方法CombionRemove,分别向代理链中添加代理实例和删除代理实例。

9)代理的定义必须放在任何类的外面,如delegate int MyDelegate();而在类的方法中调用MyDelegate d = new MyDelegate(MyClass.MyMethod);来实例化自定义代理的实例。

10)代理三步曲:

a.生成自定义代理类:delegate int MyDelegate();

b.然后实例化代理类:MyDelegate d = new MyDelegate(MyClass.MyMethod);

c.最后通过实例对象调用方法:int ret = d();

 

复制代码
using  System;
public   class  InstanceMethod
{
    
public   int  Result1( string  name)
    {
        Console.WriteLine(
" Instance: "   +  name);
        
return   1 ;
    }

    
public   static   void  Result2()
    {
        Console.WriteLine(
" Static " );
    }
}

public   class  class1
{
    
public   delegate   int  DelHandler( string  name);
    
public   delegate   void  DelHandler2();

    
public   static  DelHandler2 Del  =   null ;

    
public   static   void  Main( string [] args)
    {
        InstanceMethod ins 
=   new  InstanceMethod();

        DelHandler del 
=   new  DelHandler(ins.Result1);
        
int  result  =  del( " a " );
        Console.WriteLine(result.ToString());

        DelHandler2 del2 
=   new  DelHandler2(InstanceMethod.Result2);
        del2();

        Del 
+=  Display;
        Del();

        Console.ReadLine();
    }

    
private   static   void  Display()
    {
        Console.WriteLine(
" Good Night! " );
    }
}
复制代码

 

 二:event

 例1

事件是特殊类型的多路广播委托,仅可从声明它们的类或结构(发行者类)中调用。如果其他类或结构订阅了该事件,则当发行者类引发该事件时,会调用其事件处理程序方法。

可以看出,C#中的event是通过delegate实现的,event 只是在delegate基础上作了两点限制:
1:客户只能使用委托所提供的+=和-=行为,不能直接用=,即不影响委托对其他observer的notify.
2:只有声明类可以调用(或激发)一个事件,外部类不可以直接调用其事件。
在一个C#接口中可以包容方法、C#属性以及索引器,也可以包容事件,但不能包含委托,因为接口定义的是成员,委托定义的是一个类型,而事件同样是成员。

复制代码
using  System;

namespace  CSharpConsole
{
    
// http://topic.csdn.net/t/20040903/17/3338252.html
    
// 定义委托处理程序
     public   delegate   void  PlayGameHandler( object  sender, System.EventArgs e);

    
public   class  场景
    {
        [STAThread]
        
public   static   void  Main( string [] args)
        {
            Console.WriteLine(
" 场景开始了. " );
            
// 生成小王

            小王 w 
=   new  小王();
            
// 生成小账
            小张 z  =   new  小张();

            
// 指定监视
            z.PlayGame  +=   new  PlayGameHandler(w.扣钱);

            
// 开始玩游戏
            z.玩游戏();

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

    
//    负责扣钱的人   
     public   class  小王
    {
        
public  小王()
        {
            Console.WriteLine(
" 生成小王 " );
        }

        
public   void  扣钱( object  sender, EventArgs e)
        {
            Console.WriteLine(
" 小王:好小子,上班时间胆敢玩游戏 " );
            Console.WriteLine(
" 小王:看看你小子有多少钱 " );
            小张 f 
=  (小张)sender;
            Console.WriteLine(
" 小张的钱: "   +  f.钱.ToString());
            Console.WriteLine(
" 开始扣钱 " );
            System.Threading.Thread.Sleep(
500 );
            f.钱 
=  f.钱  -   500 ;
            Console.WriteLine(
" 扣完了.现在小张还剩下: "   +  f.钱.ToString());
        }
    }

    
// 如果玩游戏,则引发事件
     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(
500 );
            System.EventArgs e 
=   new  EventArgs();
            OnPlayGame(e);
        }

        
protected   virtual   void  OnPlayGame(EventArgs e)
        {
            
if  (PlayGame  !=   null )
            {
                PlayGame(
this , e);
            }
        }
    }
}
复制代码

 

 例2

复制代码
using  System;

namespace  nsEventSample
{
    
///     <summary>      
    
///    类EatEventArgs必须继承自类EventArgs,用来引发事件时封装数据     
    
///     </summary>      
     public   class  EatEventArgs : EventArgs
    {
        
public  String restrauntName;     // 饭店名称
         public   decimal  moneyOut;         // 准备消费金额
    }

    
///     <summary>
    
///    这个委托用来说明处理吃饭事件的方法的方法头(模式)
    
///     </summary>
     public   delegate   void  EatEventHandler( object  sender, EatEventArgs e);

    
///     <summary>
    
///    引发吃饭事件(EateEvent)的类Master(主人),这个类必须
    
///    1.声明一个名为EatEvent的事件:public event EatEventHandler EatEvent;
    
///    2.通过一个名为OnEatEvent的方法来引发吃饭事件,给那些处理此事件的方法传数据;
    
///    3.说明在某种情形下引发事件呢?在饿的时候。用方法Hungrg来模拟。
    
///     </summary>
     public   class  Master
    {
        
// 声明事件
         public   event  EatEventHandler EatEvent;

        
// 引发事件的方法
         public   void  OnEatEvent(EatEventArgs e)
        {
            
if  (EatEvent  !=   null )
            {
                EatEvent(
this , e);
            }
        }

        
// 当主人饿的时候,他会指定吃饭地点和消费金额。
         public   void  Hungry(String restrauntName,  decimal  moneyOut)
        {
            EatEventArgs e 
=   new  EatEventArgs();
            e.restrauntName 
=  restrauntName;
            e.moneyOut 
=  moneyOut;

            Console.WriteLine(
" 主人说: " );
            Console.WriteLine(
" 我饿了,要去{0}吃饭,消费{1}元 " , e.restrauntName, e.moneyOut);

            
// 引发事件     
            OnEatEvent(e);
        }
    }

    
///     <summary>      
    
///    类Servant(仆人)有一个方法ArrangeFood(安排食物)来处理主人的吃饭事件
    
///     </summary>      
     public   class  Servant
    {
        
public   void  ArrangeFood( object  sender, EatEventArgs e)
        {
            Console.WriteLine();
            Console.WriteLine(
" 仆人说: " );
            Console.WriteLine(
" 我的主人,   您的命令是   :    " );
            Console.WriteLine(
" 吃饭地点   --   {0} " , e.restrauntName);
            Console.WriteLine(
" 准备消费   --   {0}元    " , e.moneyOut);
            Console.WriteLine(
" 好的,正给您安排。。。。。。。。 " );
            Console.WriteLine(
" 主人,您的食物在这儿,请慢用 " );
        }
    }

    
///     <summary>      
    
///    类God安排qinshihuang(秦始皇)的仆人是lisi(李斯),并让李斯的ArrangeFood     
    
///    方法来处理qinshihuang的吃饭事件:qinshihuang.EatEvent   +=   new   EatEventHandler(lishi.ArrangeFood);     
    
///     </summary>      
     public   class  God
    {
        
public   static   void  Main()
        {
            Master qinshihuang 
=   new  Master();
            Servant lishi 
=   new  Servant();

            qinshihuang.EatEvent 
+=   new  EatEventHandler(lishi.ArrangeFood);

            
// 秦始皇饿了,想去希尔顿大酒店,消费5000元
            qinshihuang.Hungry( " 希尔顿大酒店 " 5000.0m );

            Console.ReadLine();
        }
    }
}
复制代码

 

例3

复制代码
using  System;
using  System.Collections.Generic;
using  System.Text;
using  System.Threading;

namespace  ConsoleApplication8
{
    
// http://www.cnblogs.com/xinjunjie/archive/2006/08/01/464944.html
     public   class  Program
    {
        
static   void  Main( string [] args)
        {
            Clock c 
=   new  Clock();
            DisplayClock dis 
=   new  DisplayClock();
            dis.Subscribe(c);
            c.Run();
        }
    }

    
public   class  DisplayClock
    {
        
public   void  Subscribe(Clock clk)
        {
            clk.OnClock 
+=   new  Clock.DisplayClock(clk_OnClock);
        }

        
void  clk_OnClock( object  clock, TimerEventArgs args)
        {
            Console.WriteLine(args.hour 
+   " : "   +  args.minutes  +   " : "   +  args.second);
        }
    }

    
public   class  Clock
    {
        
private   int  hour;
        
private   int  minutes;
        
private   int  second;

        
public  Clock()
        {
            hour 
=  DateTime.Now.Hour;
            minutes 
=  DateTime.Now.Minute;
            second 
=  DateTime.Now.Second;
        }

        
public   delegate   void  DisplayClock( object  clock, TimerEventArgs args);
        
public   event  DisplayClock OnClock;

        
public   void  Run()
        {
            
while  ( 1   ==   1 )
            {
                Thread.Sleep(
100 );
                DateTime dt 
=  DateTime.Now;
                
if  (dt.Second  !=  second)
                {
                    
if  (OnClock  !=   null )
                    {
                        OnClock(
this new  TimerEventArgs(hour, minutes, second));
                    }
                }
                hour 
=  DateTime.Now.Hour;
                minutes 
=  DateTime.Now.Minute;
                second 
=  DateTime.Now.Second;
            }
        }
    }

    
public   class  TimerEventArgs : EventArgs
    {
        
public   readonly   int  hour;
        
public   readonly   int  minutes;
        
public   readonly   int  second;

        
public  TimerEventArgs( int  h,  int  m,  int  s)
        {
            
this .hour  =  h;
            
this .minutes  =  m;
            
this .second  =  s;
        }
    }
}
复制代码

 

例4

 

复制代码
using  System;
using  System.Collections.Generic;
using  System.Text;

namespace  UsingEvent
{
    
public   delegate   void  ClickEventHandler( object  sender, EventArgs e); // 声明一个代表:请看文章最后面Note

    
public   class  MyButton               // 创建MyBottom
    {
        
public   event  ClickEventHandler ClickEvent; // 声明一个事件

        
public   void  Click()                                  // 单击MyButton
        {
            
if  (ClickEvent  !=   null )
            {
                Console.WriteLine(
" MyButton: 我被单击了 " );
                ClickEvent(
this null );                           // 抛出事件,给所有相应者
            }
        }
    }

    
public   class  MyForm
    {
        
public  MyButton myButton  =   new  MyButton();
        
public  MyForm()
        {
            
// 添加事件到myButton中,当myButton被单击的时候就会调用相应的处理函数
            myButton.ClickEvent  +=   new  ClickEventHandler(OnClickEvent);
        }

        
// 事件处理函数
         void  OnClickEvent( object  sender, EventArgs e)
        {
            Console.WriteLine(
" MyForm: 我知道你被单击了! " );
        }
    }

    
class  Program
    {
        
static   void  Main( string [] args)
        {
            MyForm form 
=   new  MyForm(); // 生成一个MyForm
            form.myButton.Click(); // 单击MyForm中的鼠标,效果就出来了
            Console.ReadLine();
        }
    }
}

复制代码

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值