C#--- 之"委托、事件" 面面观

委托与事件,它们的应用非常广泛,为了便于复习,我特地将它们总结了一下。

 

一、委托

委托,通俗的讲,就是‘方法’的容器。

是用来存放和调用方法用的。

 

下面这个例子,简单的介绍一下委托的用法:

 

public delegate void  SayHi_Delegate(string name);


这就是一个委托,任何形如 void **(string **);的函数,都可以使用这个委托来调用。

比如:

    private static void SayHiEn(string name)   //说英文
        {
            Console.WriteLine("Hi  {0}, I am ZeroCool_24!",name); 
        }

        private static void SayHiCh(string name)   //说中文
        {
            Console.WriteLine("你好  {0}, 我是 ZeroCool_24!", name); 
        }


 

调用的方法如

        static void Main(string[] args)
        {
            SayHi_Delegate Hi;
            Hi = SayHiCh;
            Hi += SayHiEn;

            Hi("cyh");

            Console.ReadLine(); 
        }

输出结果为:


可以看到,在向SayHi方法容器中添加方法的时候,使用了 += ,同理还可以用 -=; 值得注意的一点是,容器内必须至少要有一个方法。

 

我们知道,可以将方法作为参数来传递,同理的,我们也可以将委托(方法的容器),作为参数来进行传递,如下:

首先定义接收委托的函数

private static void GreetPeople(string name, SayHi_Delegate MakeGreeting)
        {
            MakeGreeting(name);
        }

 

接下来调用一下:

 

      static void Main(string[] args)
        {
            SayHi_Delegate Hi;
            Hi = SayHiCh;
            Hi += SayHiEn;

            //Hi("cyh");    
            GreetPeople("cyh", Hi); 

            Console.ReadLine(); 
        }


得到的结果与上面是一样的。

二、事件

由于大家已经对委托有了一个初步的认识,下面,我们将这个例子做一个改进

using System;

namespace 委托
{ 
    public delegate void SayHi_Delegate(string name);  
 
    public class GreetClass	//新的类
    {
        public void GreetPeople(string name, SayHi_Delegate MakeGreeting)
        {
            MakeGreeting(name);
        }
    }
}


 

using System;

namespace 委托
{
    class TestClass
    {
        private static void SayHiEn(string name)
        {
            Console.WriteLine("Hi  {0}, I am ZeroCool_24!",name); 
        }

        private static void SayHiCh(string name)
        {
            Console.WriteLine("你好  {0}, 我是 ZeroCool_24!", name); 
        }

        static void Main(string[] args)
        {
            SayHi_Delegate Hi;
            Hi = SayHiCh;
            Hi += SayHiEn;

            //Hi("cyh");    
            GreetClass greet = new GreetClass();    
            greet.GreetPeople("cyh", Hi);

            greet.GreetPeople("zc", SayHiCh);   //SayHiCh()非 SayHi_Delegate 委托,但是因为可以转化成一个只含SayHiCh()方法的委托。

            Console.ReadLine(); 
        }
    }
}



结果如下:

 

演示的结果很好,看上去好像也没有问题。但是,能不能更好的处理呢?

答案是肯定的。应用面向对象的思想,我们应该将main()中的委托变量Hi封装到GreetClass类中。

using System;

namespace 委托
{ 
    public delegate void SayHi_Delegate(string name);  
 
    public class GreetClass
    {
        public  SayHi_Delegate Hi;      //SayHi_Delegate的实例

        public void GreetPeople(string name, SayHi_Delegate MakeGreeting)
        {
            MakeGreeting(name);
        }
    }
}

static void Main(string[] args)
        {
             GreetClass greet = new GreetClass();
            greet.Hi = SayHiCh;
            greet.Hi += SayHiEn;

            greet.GreetPeople("Cyh_Zc",greet.Hi);    

            Console.ReadLine(); 
        }





这样,又有问题出来了,委托变量Hi虽然被封装到了GreetClass,但是,客户端依然可以随意的访问它,这就带来了一个安全性的问题,如果现在的Hi不是SayHi_Delegate类型,而是string类型,我们可以使用属性来解决

但是,委托变量该如何解决呢?

没错,就是事件。Event,它封装了委托类型的变量,使得:在类的内部,不管你声明它是public还是protected,它总是private的。在类的外部,注册“+=”和注销“-=”的访问限定符与你在声明事件时使用的访问符相同。

 

我们来改写GreetClass类:
 

public class GreetClass
    {
        public event SayHi_Delegate Hi;      //SayHi_Delegate的事件,只是修饰符多了一个event		        			public void GreetPeople(string name)
        {
            Hi(name);
        }
    }
static void Main(string[] args)
        {
            GreetClass greet = new GreetClass();
            //greet.Hi = SayHiCh;   //编译错误									 	    	   greet.Hi += SayHiCh;
            greet.Hi += SayHiEn;

            greet.GreetPeople("Cyh_Zc");    

            Console.ReadLine(); 
        }


 

可以看到,在使用greet.Hi = SayHiCh的使用,出现了编译错误,这是为什么呢?

原来 greet.Hi 只能出现在 += 或 -= 的左边(从类“GreetClass”中使用时除外)。为什么会这样?

我们用.NET Reflector来看看GreetClass编译后的过程:

public class GreetClass
{
    // Fields
    private SayHi_Delegate Hi;

    // Events
    public event SayHi_Delegate Hi;

    // Methods
    public GreetClass();
    public void GreetPeople(string name);
}

看到上面编译之后的代码,大家会疑惑,为什么会多了一个(Fields)  private SayHi_Delegate Hi;

原来,public event SayHi_Delegate Hi;这行代码编译之后,会默认自动的新增一个private的字段SayHi_Delegate Hi;

 

猜想:这个private SayHi_Delegate类型的字段Hi是在GreetClass中使用的,Public 事件 Hi是供外部使用的。

验证:查看GreetClass中的Hi(name);           

发现引用的是:

private SayHi_Delegate Hi;

再查看Main()方法中的greet.Hi,可以看到:

public event SayHi_Delegate Hi
{
    add
    {
        SayHi_Delegate delegate3;
        SayHi_Delegate hi = this.Hi;
        do
        {
            delegate3 = hi;
            SayHi_Delegate delegate4 = (SayHi_Delegate) Delegate.Combine(delegate3, value);
            hi = Interlocked.CompareExchange<SayHi_Delegate>(ref this.Hi, delegate4, delegate3);
        }
        while (hi != delegate3);
    }
    remove
    {
        SayHi_Delegate delegate3;
        SayHi_Delegate hi = this.Hi;
        do
        {
            delegate3 = hi;
            SayHi_Delegate delegate4 = (SayHi_Delegate) Delegate.Remove(delegate3, value);
            hi = Interlocked.CompareExchange<SayHi_Delegate>(ref this.Hi, delegate4, delegate3);
        }
        while (hi != delegate3);
    }
}

 

说明了上面的猜想是正确的。

看到event Hi()中,有两个方法:add()和remove(),我想大家应该已经猜到,这两个函数对应了greet.Hi的两个操作 += 和 -= 。这下我想大家也应该知道了为什么greet.Hi = SayHiCh;的时候会报错了------因为,Hi事件中没有与 ‘=’对应的方法。

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值