C#基础---事件的使用

   一:什么是事件

       事件是可以被控件识别的操作,如按下确定按钮,选择某个单选按钮或者复选框。每一种控件有自己可以识别的事件,如窗体的加载、单击、双击等事件,编辑框(文本框)的文本改变事件,等等。事件在桌面应用程序里面无处可见,比如winform,WPF。。。,其次事件是基于委托而产生的。

二:事件的基本使用

     1.事件的声明: 其实和委托一样只是多了一个Event而已。ShowMsg 就具备了ShowMsgHandler的功能。

    Notes: 1. 委托可以依赖于一个类或者一个域名空间(C#基础---委托的使用,里面我有提到过), 而event必须依赖于一个类。否者无法声明。

        2. 委托可以用【=号】,而事件中只能用【+】或者【-】实现对方法的添加和删除。当事件为空的时候调用【-】方法不会报错。

        

public delegate void ShowMsgHandler(string str);
public event ShowMsgHandler ShowMsg;

     2.事件基本使用: 其实基本用法和委托差不多。这里有一点要说明,其实可以通过判断是否为Null,来确定是否已经注册了方法到事件或者委托。这个有时候勇于判断事件是否该触发。其次也发现事件第一次添加方法的时候是直接使用【+】号的,而不用像委托那样第一次使用【=】号,后面的才使用【+】号。      

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SpongeBobCoder.EventTest
{
    public delegate void ShowMsgHandler(string str);
    public class Program
    {
        public static event ShowMsgHandler ShowMsg;

        public static void ShowName(string str)
        {
            Console.WriteLine("My Name is {0}", str);
        }

        public static void Main(string[] args)
        {
            Console.WriteLine(ShowMsg == null);
            ShowMsg += ShowName;
            ShowMsg("SopongeBob");
            Console.WriteLine(ShowMsg == null);
            Console.ReadKey();
        }
    }
}

     3. 为何使用事件:

其实从上面来看事件和委托差不多。用法没啥区别,但是为何还要使用委托呢? 在Codeplex看到的一篇文档感觉挺不错的。http://www.codeproject.com/Articles/7316/Events-and-Delegates ;小弟英语不好,英语好的就去看看原文,我的大概理解是: 好比一个App由一个项目组在开发,必定有一个项目经理,而这个项目经理的下面会有很多帮他做事的员工,其实经理就好比是一个委托,而每一位工作的员工就好比委托注册的方法。项目开发完成了,拿给了用户,用户安装好了。可是用户发现软件有缺陷,需要改进,而往往这个时候用户是不会直接跟开发组的项目经理接触的。往往会把意见给 维护部门,而由维护部门来告知相应开发组的项目经理。而通知这段过程就叫做事件。事件可以用来更好的封装,和管理委托的。个人理解,这就是事件为何基于特定类的。不同类的 事件可以绑定同一个委托,从而注册不同的方法。

      3.1 先什么一个Publisher类:类中有一个事件CalculatorEvent 和一个方法DoSomething,若事件被添加方法后,那么将执行

    

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SpongeBobCoder.EventTest
{
    public class Publisher
    {
        public event CalculatorHandler CalculatorEvent;

        public void DoSomething(double num1,double num2)
        {
            if (CalculatorEvent != null)
            {
                CalculatorEvent(num1, num2);
            }
        }
    }
}

    3.2 然后看Program类,Main方法里面声明了两个Publisher对象,分别是A,B。分别添加了AddNum和SumNum,运行结果是 3 和 -1。通过一个Publisher类可以对委托进行管理。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SpongeBobCoder.EventTest
{
    public delegate void CalculatorHandler(double num1,double num2);

    public class Program
    {
        public static void AddNum(double num1, double num2)
        {
            Console.WriteLine("两数之和为:{0}", num1 + num2);
        }

        public static void SubNum(double num1, double num2)
        {
            Console.WriteLine("两数之差为:{0}", num1-num2);
        }

        public static void Main(string[] args)
        {
            Publisher pubA = new Publisher();
            Publisher pubB = new Publisher();
            pubA.CalculatorEvent += AddNum;
            pubB.CalculatorEvent += SubNum;

            pubA.DoSomething(1, 2);
            pubB.DoSomething(1, 2);

            Console.ReadKey();
        }
    }
}

三:事件的使用

    1.异常处理: 事件可以注册多个方法,可是要是其中有一个方法抛出异常了怎么办,一旦抛出异常了。那么当前执行程序被中断,那么后面注册的方法就没法执行了。问题来了,那些方法可以解决这个问题呢?  方法一,保证所注册的方法不会出现异常,这一块我们是无法预知。 方法二。将注册的方法的异常都吃掉,其中微软提供了两个方法GetInvocationList和DynamicInvoke方法:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SpongeBobCoder.EventTest
{
    public class Publisher
    {
        public event CalculatorHandler CalculatorEvent;

        public void DoSomething(double num1, double num2)
        {
            if (CalculatorEvent != null)
            {
                Delegate[] delArray = CalculatorEvent.GetInvocationList(); //获取到所有的委托方法.
                foreach (Delegate del in delArray)
                {
                    try
                    {
                       object obj = del.DynamicInvoke(num1, num2); //obj是获取每个方法的返回值,如果声明的是无返回值的委托,那么obj==null
                       Console.WriteLine(obj == null);
                    }
                    catch (Exception e) // 把异常吃掉
                    {
                        Console.WriteLine(e.InnerException.Message);
                    }
                }
            }
        }
    }
}

  2.异步调用:对于前面的注册的时间,都是顺序执行,那如何实现异步执行呢,各个注册之间不相互干扰,其实微软提供了一个BeginInvoke方法可以解决这个问题.

Public IAsyncResult BeginInvoke (
    InvokeArgs invokeArgs, // 这一部分对于的是委托
    AsyncCallback callback,// 回调方法,注册方法执行完后,将会执行回调方法
    Object userState // 传递的参数
)

上面理解起来可能有点困难下面来看看代码吧:
            Program类: AddNum 方法有5秒的延时。SubNum没有添加延时,注册顺序是 AddNum,SubNum

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;

namespace SpongeBobCoder.EventTest
{
    public delegate double CalculatorHandler(double num1, double num2);

    public class Program
    {
        public static double AddNum(double num1, double num2)
        {
            Thread.Sleep(TimeSpan.FromSeconds(5));
            Console.WriteLine("两数之和为:{0}", num1 + num2);
            return num1 + num2;
        }

        public static double SubNum(double num1, double num2)
        {
            Console.WriteLine("两数之差为:{0}", num1 - num2);
            return num1 - num2;
        }

        public static void Main(string[] args)
        {
            Publisher pubA = new Publisher();
            pubA.CalculatorEvent += AddNum;
            pubA.CalculatorEvent += SubNum;
            pubA.DoSomething(1, 5);
            Console.ReadKey();
        }
    }
}

    publisher类: 注意看红色方法,前面两个参数是与委托对应的。后面的MyCallBack是回调的方法,

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Remoting.Messaging;
using System.Text;
using System.Threading.Tasks;

namespace SpongeBobCoder.EventTest
{
    public class Publisher
    {
        public event CalculatorHandler CalculatorEvent;

        public void DoSomething(double num1, double num2)
        {
            if (CalculatorEvent != null)
            {
                Delegate[] delArray = CalculatorEvent.GetInvocationList(); //获取到所有的委托方法.
                foreach (Delegate del in delArray)
                {
                    try
                    {
                        CalculatorHandler handler = del as CalculatorHandler;
                        IAsyncResult myResult = handler.BeginInvoke(num1, num2, MyCallback, "方法执行完毕,回调成功" + handler.Method.Name);
                        // Console.WriteLine("SpongeBob"); 这块代码不注释的话是先执行这段代码的输出,然后才会输出其他的。这一块我不知道为什么
                    }
                    catch (Exception e) // 把异常吃掉
                    {
                        Console.WriteLine(e.InnerException.Message);
                    }
                }
            }
        }

        public void MyCallback(IAsyncResult asyncResult)
        {
            AsyncResult result = (AsyncResult)asyncResult;
            CalculatorHandler handler = (CalculatorHandler)result.AsyncDelegate;
            Console.WriteLine(asyncResult.AsyncState);
            Console.WriteLine("获取到执行结果为:{0} \n", handler.EndInvoke(asyncResult));
        }
    }
}

运行结果: 其实先执行的是 SubNum,已经达到了异步的效果,其中通过EndInvoke也在回调函数中获取到了委托的返回值。
     

codezip:http://files.cnblogs.com/FourLeafCloverZc/CSharp.zip

总结:  以前最开始的对事件理解不清楚,记得当时在winform的时候跨线程获取数据就要用Invoke来获取,不然老是提示线程不安全。本次博客中我发现了两个问题需要个各位博友帮忙解答

1. 在异步调用的代码中有一段代码被我注解了。其实我发现了一个问题, 如果 Console.WriteLine("SpongeBob"); 不注解,运行的情况是,先输出两行(“SpongeBob”) 然后再输出 上图的运行结果。不清楚是什么原因,求大神指点。

2. 对于下面代码如果调用的传入的参数是1,0 ; 是不会报错的。运行结果是两数相除无穷大。应该会报除数不能为0的异常呀。

        public static void DivNum(double num1, double num2)
        {
            Console.WriteLine("两数相除为:{0}", num1 / num2);
        }
时间: 2024-10-03 20:52:39

C#基础---事件的使用的相关文章

基础事件(一)

JavaScript有一个非常重要的功能,就是事件驱动.当页面完全加载后,用户通过鼠标或键盘触发页面中绑定事件的元素即可触发.jQuery为开发者更有效率的编写事件行为, 封装了大量有益的事件方法供我们使用. 绑定事件 在JavaScript课程的学习中,我们掌握了很多使用的事件,常用的事件有:click.dblclick.mousedown.mouseup.mousemove.mouseover.mouseout.change. select.submit.keydown.keypress.k

android ViewGroup和View触屏基础事件OnTouch处理

分发事件:dispatchTouchevent 截断事件:interceptTouchEvent 触摸事件:OnTouch 父视图的分发事件返回true,父视图处理触摸事件事件,父视图触摸事件返回false,由所在的activity处理:返回true,仍然可以接收到触摸事件: 父视图的分发事件返回false,则截断事件触发: 截断事件返回true,自己处理触摸事件,不交由子视图处理: 截断事件返回false,子视图分发事件: 截断事件false,处理事件true,子视图处理事件为true,则父视

jQuery基础——事件篇

jQuery基础--事件篇 鼠标事件 click与dbclick事件 click方法监听用户单击操作,dbclick监听用户双击操作,这两个方法类似. dbclick与click事件不同的是: click事件触发需要以下几点: click事件其实是由mousedown与mouseup 2个动作构成,所以点击的动作只有在松手后才触发 dblclick事件触发需要以下几点: dblclick又是由2个click叠加而来的,所以dblclick事件只有在满足以下条件的情况下才能被触发 鼠标指针在元素里

javascript基础-事件1

原理 事件分两种.第一种浏览器事件,由浏览器抛出事件,它是人机交互的基础:第二种自定义事件,由程序员抛出事件,它是模拟事件流程.两者都是为了完成数据的传递. 浏览器事件 机制 冒泡和捕获两种机制.因IE8-前只支持冒泡,所以实际运用中,只允许事件冒泡. 处理方式 IE8- 与 IE9+.标准浏览器事件对象的区别 浏览器事件类型与处理单独放一节 自定义事件 通常用在  DOM渲染绑定自定义事件如'changeattr' --> 数据源(json对象属性)变更,抛出'changeattr'事件---

JS基础——事件对象event

事件为什么会是对象呢?先了解一下事件处理 一.事件处理 JS在事件处理函数中提供了事件对象,帮助处理鼠标和键盘事件.同时还可以修改一些事件的捕获和冒泡流的函数. 事件处理分为三部分:对象.事件处理函数=函数 document.onclick=function(){ alert(this.value); //this代表着在该作用域中离它最近的对象. } 以上事件处理中,document为对象,click是事件处理类型,onclick为事件处理函数.function()为匿名函数,用于触发后执行.

JS基础——事件绑定

上一篇博客JS事件对象中,老师问JS事件处理和VB中的事件处理有什么联系?先来解决一下这个问题.举个VB.net中事件处理的例子(JS敲久了,VB习惯的都不熟悉了,看来得经常回顾了): 1.事件处理VB VS JS Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click MsgBox("helo!") MsgBox(sender.width) '弹出触发这个事件对象的宽度 Msg

UI基础:事件.响应链

UIEvent:事件,是由硬件捕捉的一个代表用户操作操作设备的对象. 事件分三类:触摸事件.晃动事件.远程控制事件. 触摸事件:用户通过触摸设备屏幕操作对象,.输入数据.支持多点触摸,包含1个到多个触摸点. UIView支持触摸事件(继承了UIResponder),而且支持多点触摸 使用时,需要定义UIView子类,重写触摸相关的方法. 1.刚开始触摸到屏幕的时候触发 -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{

jquery基础事件

一.常用的事件有:click.dblclick. mousedown.mouseup.mousemove.mouseover.mouseout.change.select.submit.keydown. keypress.keyup.blur.focus.load.resize.scroll.error..... 二.Query 通过.bind()方法来为元素绑定这些事件.可以传递三个参数:bind(type,[data],fn): type 表示一个或多个类型的事件名字符串. [data]是可

Linux内核基础--事件通知链(notifier chain)

转载: http://blog.csdn.net/wuhzossibility/article/details/8079025 http://blog.chinaunix.net/uid-27717694-id-4286337.html 内核通知链 1.1. 概述 Linux内核中各个子系统相互依赖,当其中某个子系统状态发生改变时,就必须使用一定的机制告知使用其服务的其他子系统,以便其他子系统采取相应的措施.为满足这样的需求,内核实现了事件通知链机制(notificationchain). 通知