C# 事件

事件,我相信开始学C#的朋友都会用过,在C#中很常见,比如点击一个按钮,上传一张图片等等,在WinForm或WebForm中都在使用着事件。今天,趁着有少少事件,我决定来重温一下之前被自己略过的东西 - 事件。

  好记得在之前,在用一个方法的时候,如果参数里面有个Handler,就好害怕,其实事件还是用委托来做中介的,在事件上两次转到定义就去到委托了,将委托复制出来,去掉delegate就是方法签名了,写上自己要实现的代码给事件赋值就OK了。

  一、什么是事件

  事件涉及两类角色:事件的发布者和事件的订阅者。触发事件的对象称为事件发布者,捕获时间并对其作出响应的对象叫做事件订阅者。

  二、事件和委托的关系

  在事件触发以后,事件发布者需要发布消息,通知事件订阅者进行事件处理,但事件发布者并不知道要通知哪些事件订阅者,这就需要在发布者和订阅者之间存在一个中介,这个中介就是委托。我们知道,委托都有一个调用列表,那么,只需要事件发布者有这样一个委托,各个事件订阅者将自己的事件处理程序都加入到该委托的调用列表中,那么事件触发时,发布者只需要调用委托即可触发订阅者的事件处理程序。

  三、如何声明事件

  声明事件的语法和定义一个类的成员非常相似,也非常简单。事实上,事件就是类成员的一种,只是事件定义中包含一种特殊的关键字:event。

  事件的声明有两种方式:

  1、采用自定义委托类型。

  2、采用EventHandler预定义委托类型。

  这两种方式基本相同,只不过第二种是.Net Framework中普遍采用的一种形式,因此建议尽量采用第二种方式。

    //     关键字  委托类型     时间名
    public event  EventHandler PrintComplete;

  EventHandler是在BCL中预定义的委托类型,它位于System命名空间,用以处理不包含事件数据的事件。事件如果需要包含事件数据,可以通过派生EventArgs实现。

  先来看看EventHandler委托的签名:

public delegate void EventHandler(Object sender,EventArgs e);

  1、委托的返回类型为void;

  2、第一个参数--sender参数,它负责保存触发事件的对象的引用,因为参数的类型是Object类型,因此它可以保存任何类型的实例;

  3、第二个参数--e参数,它负责保存事件数据,这里是在BCL中定义的默认的EventArgs类,它位于System命名空间中,他不能保存任何数据。

  四、订阅事件

  事件订阅者角色需要订阅事件发布者发布的事件,这样才能在事件发布时接受到消息并作出响应,事件事实上是委托类型,因此事件处理方法必须和委托签名相匹配。如果事件使用预定义的委托类型:EventHandler,那么匹配它的事件处理方法如下:

    public void SomeEventHandler(object sender, EventArgs e)
    {
        //..
    }

  有了事件处理方法,就可以订阅事件了,只需要使用加法赋值运算符(+=)即可。

  五、触发事件

    //检查事件是否为空
    if (PrintComplete != null)
    {
        //像调用方法一样触发事件,参数
        PrintComplete(this,new EventArgs();
    }

  完整的一个事件例子:

namespace ConsoleApplication1
{
    public class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("该做的东西做完,然后触发事件!");
            EventSample es = new EventSample();
            es.ShowComplete += es.MyEventHandler;
            es.OnShowComplete();

            Console.ReadKey();
        }
    }

    public class EventSample
    {     //定义一个事件
        public event EventHandler ShowComplete;         //触发事件
        public void OnShowComplete()
        {
            //判断是否绑定了事件处理方法,null表示没有事件处理方法
            if (ShowComplete != null)
            {          //像调用方法一样触发事件
                ShowComplete(this, new EventArgs());
            }
        }

        //事件处理方法
        public void MyEventHandler(object sender, EventArgs e)
        {
            Console.WriteLine("谁触发了我?" + sender.ToString());
        }
    }
}

  六、使用和扩展EventArgs类

  前面提到过,默认的预定义委托EventHandler的第二个参数,本身不能包含事件数据。但是在很多.net提供的方法,都能用e调用出某些信息这是因为这不是默认的EventArgs类了。因此,在事件引发时不能向事件处理程序传递状态信息,如果要想传递状态信息,则需要从此类派生出一个类来保存信息。

  以下为扩展EventArgs类的示例:

    public class PrintEventArgs : EventArgs
    {
        public string PrintState
        {
            get;
            set;
        }

        public PrintEventArgs(string state)
        {
            PrintState = state;
        }
    }

  而在调用时,只需将EventArgs换成PrintEventArgs

    public void SomeEventHandler(object sender, PrintEventArgs e)
    {
        Console.WriteLine("打印已完成!");
    }

  但是要注意此时,绑定事件编译器会报错:

  enentSample.PrintComplete += ShowMessage;  //此行代码编译器报错

  为什么呢?因此事件委托的第二个参数是EventArgs类型与扩展的PrintEventArgs不同,因此不能在绑定旧的方法;所以就要使用自定义委托了。

  七、自定义委托

  既然EventHandler委托不能使用了,那么就只有考虑使用自定义委托来声明事件了。首先声明一个自定义委托:

  public delegate void PrintEventDelegate(object sender,PrintEventArgs e);

  接下来,将事件声明中使用的预定义委托EventDelegate换成我们自定义的委托:

  public event PrintEventDelegate PrintComplete;

  因为我们扩展的PrintEventArgs类没有不带参数的构造函数,因此需要修改事件触发部分的代码,传递一个参数进去,该参数的值就是要发送给事件处理方法的状态信息,这里以一个简单的字符串代替:

  if(PrintComplete != null)
  {
    PrintComplete(this,new PrintEventArgs("测试消息"));
  }

  八、事件访问器

  事件是特殊的多路广播委托,事件默认有一个私有的委托类型变量,用以保存对订阅事件的事件处理方法的引用,此委托类型的变量仅能从声明该事件的类中委托。事件订阅者通过提供对事件处理方法的引用来订阅事件,这些方法通过默认的时间访问器添加到委托的调用列表中。这里的事件访问器类似于属性访问器,不同之处在于,时间访问器被命名为add和remove,而不是属性的get和set。在大多数情况下都不需要提供自定义的事件访问器。如果没有提供,则编译器会自动添加事件访问器。如果需要添加自定义事件访问器,以支持某些自定义行为,可以使用如下语法:

  public event MyEventHandler PrintComplete
  {
    add
    {
      //..
    }
    remove
    {
      //..
    }
  }

  在声明了事件访问器以后,编译器将不会提供私有的委托对象,此时对于订阅者事件处理方法引用的管理需要我们自己去实现。

    public event MyEventHandler PrintComplete
    {
        add { myEventHandler += value; }
        remove { myEventHandler -= value; }
    }

  下面给出一个扩展EventArgs与自定义委托,传递数据到方法的示例:

namespace ConsoleApplication1
{
    public class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("该做的东西做完,然后触发事件!");
            EventSample es = new EventSample();
            es.ShowComplete += es.MyEventHandler;
            es.OnShowComplete();

            Console.ReadKey();
        }
    }

    public class EventSample
    {
        //此事件已不能如此使用
        //public event EventHandler ShowComplete;

        //自定义委托
        public delegate void ShowEventDelegate(object sender,ShowEventArgs e);

        //将事件中的委托换成自己的自定义委托
        public event ShowEventDelegate ShowComplete; 

        public void OnShowComplete()
        {
            //判断是否绑定了事件处理方法,null表示没有事件处理方法
            if (ShowComplete != null)
            {
                //这次要传递参数数据了
                ShowComplete(this, new ShowEventArgs("传给你的数据,接着吧!"));
            }
        }

        //事件处理方法,注意第二个参数
        public void MyEventHandler(object sender, ShowEventArgs e)
        {
            Console.WriteLine("谁触发了我   " + sender.ToString());
            Console.WriteLine("传过来什么数据:   " + e.ShowResult);
        }
    }

    //自定义EventArgs
    public class ShowEventArgs : EventArgs
    {
        public string ShowResult
        {
            get;
            set;
        }

        public ShowEventArgs(string result)
        {
            ShowResult = result;
        }
    }
}

  输出结果如下:

  

C# 事件,布布扣,bubuko.com

时间: 2024-11-10 01:31:24

C# 事件的相关文章

移动端点击事件全攻略,有你知道与不知道的各种坑

看标题的时候你可能会想,点击事件有什么好说的,还写一篇攻略?哈哈,如果你这么想,只能说明你too young to simple. 接触过移动端开发的同学可能都会面临点击事件的第一个问题:click事件的300ms延迟响应.不能立即响应给体验造成了很大的困扰,因此解决这个问题就成为了必然. 这个问题的解决方案就是: zepto.js的tap事件.tap事件可以理解为在移动端的click事件,而zepto.js因为几乎完全复制jQuery的api,因此常常被用在h5的开发上用来取代jquery.

js监控微信浏览器的自带的返回事件

pushHistory(); window.addEventListener("popstate", function(e) { e.preventDefault(); //alert("我监听到了浏览器的返回按钮事件啦"); //根据自己的需求实现自己的功能 //window.location = 'http://www.baidu.com'; pushHistory(); }, false); function pushHistory() { var state

微信小程序学习总结(2)------- 之for循环,绑定点击事件

最近公司有小程序的项目,本人有幸参与其中,一个项目做下来感觉受益匪浅,与大家做下分享,欢迎沟通交流互相学习. 先说一下此次项目本人体会较深的几个关键点:微信地图.用户静默授权.用户弹窗授权.微信充值等等. 言归正传,今天分享我遇到的关于wx:for循环绑定数据的一个tips:  1. 想必大家的都知道wx:for,如下就不用我啰嗦了: <view class="myNew" wx:for="{{list}}">{{item.title}}<view

touchstart和touchend事件

touchstart和touchend事件 移动互联网是未来的发展趋势,现在国内很多互联网大佬都在争取移动这一块大饼,如微信及支付宝是目前比较成功的例子,当然还有各种APP和web运用. 由于公司的需要,最近也在开发移动web,对于一个没有移动开发经验的人来说,其实也是比较困恼的一件事情.对于移动web开发目前主要是基于webkit内核的浏览器.在webkit内核的环境下开发,你可以充分的运用html5+css3,还有它的一些私有属性.这让我很兴奋.可是,毕竟,对于一个长期习惯pc端做固定像素开

苹果手机输入中文不会触发onkeyup事件

今天同事的项目有这个问题,用我的安卓手机输入中文是ok的,但是苹果手机就不行 使用keyup事件检测文本框内容:  $('#keyup_i').bind('keyup', function(){         $('#keyup_s').text($(this).val());  } 本来是上面这种处理方式,现在改成下面这样就ok了 使用oninput以及onpropertychange事件检测文本框内容:  //先判断浏览器是不是万恶的IE,没办法,写的东西也有IE使用者       var

离散事件模型

0x01 代码框架逻辑 模拟内容: 1.离散事件模拟,模拟银行营业时的排队情况 2.不考虑顾客中途离开,顾客到达事件随机,业务办理时间 3.长度随机,选择最短的队排队,不再换队 代码逻辑: 1.一个事件链表,四个窗口排队队列 2.事件驱动:每有一个新的顾客到达,将产生下一个新顾客到达的新事件按时间顺序从小到大(OccurTime)插入事件链表(EventList) (如果此时窗口队列只有 一个顾客,还将产生此顾客离开事件插入事件链表中)      每有一个顾客从某一队列首离开,将产生他的后一位顾

zepto事件

1.zepto的定义 Zepto是一个轻量级的针对现代高级浏览器的JavaScript库, 它与jquery有着类似的api 2.zepto与jq的区别 ①相同点:Zepto最初是为移动端开发的库,是jQuery的轻量级替代品,因为它的API和jQuery相似,而文件更小:zepto的API大部分都能和jQuery兼容,所以用起来极其容易 ②不同点: (1).针对移动端程序,Zepto有一些基本的触摸事件可以用来做触摸屏交互(tap事件.swipe事件) (2),Dom操作的区别:添加id时jQ

15.1-全栈Java笔记:Java事件模型是什么?事件控制的过程有哪几步??

应用前边两节上一章节的内容,大家可以完成一个简单的界面,但是没有任何的功能,界面完全是静态的,如果要实现具体功能的话,必须要学习事件模型. 事件模型简介及常见事件模型 对于采用了图形用户界面的程序来说,事件控制是非常重要的. 一个源(事件源)产生一个事件并把它(事件对象)送到一个或多个监听器那里,监听器只是简单地等待,直到它收到一个事件,一旦事件被接收,监听器将处理这些事件. 一个事件源必须注册监听器以便监听器可以接收关于一个特定事件的通知. 每种类型的事件都有其自己的注册方法,一般形式为: v

RecyclerView动态添加、删除及点击事件

上一节讲解了RecyclerView的三种显示方式,本节将主要研究一下RecyclerView的动态添加.删除及其单击和长按事件的处理.我们在上一节代码的基础上进行相关操作. 一.修改适配器类MyAdapter,加入添加和删除这两个方法: public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> { private Context context; private List<String>

事件(1)

事件三要素 事件源:事件发生的来源 事件:行为(点击,触摸...) 监听器:当事件发送时,所要做的事情  onClickListener(单击事件) 组件.setOnClickListener(new OnClickListener(){ @Override public void onClick(View v) { String str=et.getText().toString(); tv.setText(str); } }); 1 public class Click extends Ac