在“C#基础之事件(1)”中已对事件有了一个大概,这里对事件进行更深入的学习。

本节按以下内容展开:

1.事件拥有者与事件响应者的关系;

2.事件订阅的多种写法;

3.事件的订阅和取消订阅;

4.多事件返回值的获取;

 

1.事件拥有者与事件响应者的关系

以下按照最常见到最不常见的关系列出来,并用示例展示:

 结合这四副图,分析下内存关系。所谓“XX者"就是指一个对象,就是new class(),对象所包含的事件、事件处理方法都是对象的成员。有了这个认识,在此用A图,这个最常用的关系来写一段“伪代码”。

public class 拥有者类
{
    public event 事件类型 事件成员变量;

    public void 某个方法(参数)
    {
        。。。
        事件成员变量(参数);//此处是调用事件处理方法
        。。。
    }
}

public class 响应者类
{
    private 拥有者类 _拥有者对象;

    public 响应者类(拥有者类 拥有者对象)
    {
        this._拥有者对象=拥有者对象;
        _拥有者对象.事件成员变量+=事件处理方法;
    }

    public string 事件处理方法(参数)
    {
        。。。
    }
}

由上面代码可知,响应者中包含拥有者时,可以通过构造方法传参形式包含进去,实际应用中不限于这种形式,还可以是new 一个拥有者对象、静态类等方式,只要是能包含进去的都是可行的。其它几个关系就更简单了。这些关系搞清楚了,才能“下笔如有神”。

下面是一个实际代码示例(网络资源非原创):

从program类中可以看到,通过调用事件拥有者dg的OnAlarm方法触发了事件,最终是调用了响应者host类的catch方法,与上图中的A的关系模型一样。

class Program
{
    static void Main(string[] args)
    {
        Dog dg = new Dog();
        Host ht = new Host(dg);
        DateTime now = new DateTime(2015, 8, 26, 23, 59, 40);
        DateTime end = new DateTime(2015, 8, 27, 0, 0, 0);
        Console.WriteLine("时间快接近深夜0时~~~~");
        while (now < end)
        {
            Console.WriteLine(now);
            Thread.Sleep(1000);
            now = now.AddSeconds(1);
        }
        //午夜零点小偷到达,看门狗引发Alarm事件
        Console.WriteLine("月黑风高的午夜: " + now);
        Console.WriteLine("小偷悄悄地摸进了主人的屋内... ");
        //自定义参数
        UserEventArgs e = new UserEventArgs(3);
        dg.OnAlarm(e);
        Console.WriteLine("请按任何键退出~");
        Console.ReadKey();
    }
}

//自定一个事件参数类
class UserEventArgs : EventArgs
{
    private int iEventArgs;
    public int Args
    {
        set { iEventArgs = value; }
        get { return iEventArgs; }
    }
    public UserEventArgs(int e)
    {
        iEventArgs = e;
    }
}

class Dog
{
    //1.声明关于事件的委托;
    public delegate void AlarmEventHandler(object sender, UserEventArgs e);
    //2.声明事件
    public event AlarmEventHandler Alarm;
    //3.编写引发事件的函数;
    public void OnAlarm(UserEventArgs e)
    {
        if (Alarm != null)
        {
            for (int k = 0; k < e.Args; k++)
            {
                Console.WriteLine("汪汪~~");
            }
            Alarm(this, e);
        }
    }
}
class Host
{
    //主人接收到信息引发的动作
    public void Catch(object sender, UserEventArgs e)
    {
        Console.WriteLine("NND小偷,别跑~");
    }
    public Host(Dog d)
    {
        d.Alarm += new Dog.AlarmEventHandler(Catch);
    }
}

 

2.事件订阅的多种写法

在“C#基础之事件(1)”中,用

 _A.GetStr += OnGetStr;

来订阅事件,但在上例中用

d.Alarm += new Dog.AlarmEventHandler(Catch);

这种形式来订阅,这两种有什么不同吗?,答案是:没有。前一种是C#2.0的方法,也是我们现在常用的方法;后一种是C#1.0的方法。大致在网络上查找了下,绝大部分示例都是用的后一种写法。在实际项目代码中,用的多的还是前一种写法,毕竟更简洁,看起来也舒服。

从后一种写法可知,其本质是new 了一个委托,并把方法当作委托的参数,参见前面“委托”章节,委托有多种写法,那么当然,除上面两种写法外,事件的订阅就同样有多种简略写法,这时的事件处理逻辑就可以直接写在{}中,而不用单独写成一个方法,实际用哪种方法看个人编码习惯和团队规范要求。现展示如下:

lambda表达式写法
Button1.Click+=(s,e)=>{

};

delegate写法
Button1.Click+=delegate(object sender,EventArgs e) {

};

匿名事件处理
Button1.Click+=delegate {

};

 

3.事件的订阅和取消订阅

事件的订阅用“+=”,取消订阅用“-=”,实际是调用了Add和remove的两个方法,想了解详情请参考《CLR via C#》,这里只从应用角度来说明。需要强调一点:如果是在非构造方法中订阅的事件,在适当的时候需取消订阅事件,如果没有取消当再次调用事件订阅时,会第二次订阅同一事件,这样事件处理方法就会执行第二次,如果执行n 次就会触发n次事件处理方法。

4.多事件返回值的获取

多事件返回值可以通过GetInvocationList方法获取,这个在实际中应用中不是很多,但有应用到。以下从网上找了个示例:

 private void button1_Click(object sender, EventArgs e)
        {
            int Number = 200;
            Publishser pub = new Publishser();
            Subscriber1 sub1 = new Subscriber1();
            Subscriber2 sub2 = new Subscriber2();
            Subscriber3 sub3=new Subscriber3();
            pub.NumberChanged += sub1.OnNumberChanged;
            pub.NumberChanged += sub2.OnNumberChanged;
            pub.NumberChanged += sub3.OnNumberChanged;
            pub.DoComething(Number);
        }

    class Publishser
    {
        public delegate int DemoEventHandler(int num);
        public event DemoEventHandler NumberChanged;
        public void DoComething(int temp)
        {
            if (NumberChanged != null)
            {
                Delegate[] delArray = NumberChanged.GetInvocationList();  
                foreach (Delegate del in delArray)
                {
                    DemoEventHandler method = (DemoEventHandler)del;
                    temp = method(temp);
                }
            }
            MessageBox.Show(temp.ToString());
        }
    }

    class Subscriber1
    {
        public int OnNumberChanged(int num)
        {
            MessageBox.Show("调用了Subscriber1类,num值为:"+ num);
            return num + 100; ;
        }
    }

    class Subscriber2
    {
        public int OnNumberChanged(int num)
        {
            MessageBox.Show("调用了Subscriber2类 num值为:"+num);
            return num+100;
        }
    }

    class Subscriber3
    {
        public int OnNumberChanged(int num)
        {
            MessageBox.Show("调用了Subcriber3类,num值为:"+num);
            return num+100;
        }
    }

运行得到的结果是:

                              调用了Subscriber1类,num值为:200

                              调用了Subscriber2类,num值为:300

                              调用了Subscriber3类,num值为:400

                              500

 

总结:事件的应用非常广泛,需作为重点来学习。

posted on 2019-10-27 17:03  七彩石头  阅读(254)  评论(0编辑  收藏  举报