• C#事件由浅至深简析


    0.简述

      C#中的事件可以说是应用相当多的一种机制,这里从浅至深了解下C#中的事件机制:

    1. 事件的简单应用
    2. 自定义事件
    3. C#事件机制
    4. 使用事件不得不注意的事

    PS公司上网机上没有IDE工具,所以,有些代码只能是简单写一下,无法给出运行截图和完整测测试项目了;大家见谅!

    1.事件的简单应用

      在VS的IDE中最简单的事件应用莫过于如下:

    1. 创建一个WINFORM项目
    2. 从工具栏中拖一个"按钮"控件到form1界面上
    3. 双击按钮控件,VS会自动创建按钮单击事件回调函数,并将函数与按钮单击事件关联,并跳转到事件回调函数button1_Click的代码编辑界面。
    4. 在button1_Click函数中,填写以下代码:
      MessageBox.Show("Hello,这是事件回调结果!");
    5. 按F5看看运行效果吧,点击按钮,弹出什么了?对的,是一个对话框:
                                  Hello,这是事件回调结果!

      这是最简单的事件应用。接下来让我们看看在代码中如何应用事件:

    1. 还是刚才那个按钮,让我们按F7切换到代码编辑界面吧。
    2. 在Form1的构造函数中,InitializeComponent();这一句下面添加我们的事件代码,使得整个构造函数是下面的样子:
      public Form1()
      {
          InitializeComponent();
          button1.MouseHove+=new EventHandler(Button1_MouseHover);
      }
    3. 哦,感谢微软的工程师们,在编写上面代码过程中,IDE会自动生成最终的事件回调函数,我们只需要填写函数内容即可:
      private void Button1_MouseHover(object sender,EventArgs e)
      {
              MessageBox.Show("嘿,你怎么还不点?");
      }
    4. OK,按F5看看运行效果吧(把鼠标放到按钮上等一会)。

    2.自定义事件

      如何在自定义的类中定义事件呢?参看下面的代码:

    public class UserEventClass
    {
        public void TriggerEvent()
        {
            OnEventTrigger();
        }
        protected virtual void OnEventTrigger()
        {
            var handler= EventTriggered;
            if(handler!=null)
            {
                handler(this,new EventArgs());
            }
        }
        public event EventHandler EventTriggered;
    }
    View Code

      PS:上面的代码效果是什么呢?调用UserEventClass 实例的TriggerEvent方法即会触发该实例的EventTriggered事件。至于为什么使用OnEventTrigger来最终完成事件的触发,会在下面做出解释。

      然后就可以象使用控件中的事件一样来使用我们自定义的事件了。

      当然,更复杂的自定义事件方式可以在博客园找到很多资料,基本上都大同小异,例如:

    C#事件(event)解析-徐洪军

      C#事件-张雪飞

      在上面的文章2中有讲解如何安全引发事件,这就是上面自定义事件代码中使用OnEventTrigger来最终完成事件的触发的原因。同时,OnEventTrigger被标为protected virtual,这样在UserEventClass的子类中,可以重写OnEventTrigger方法来进行需要的修改。

    3.C#事件机制

      所有的教材里面都会先讲解C#委托,然后才会讲解C#事件,因为事件本质上仍然是由委托完成的,其神奇的表现仅仅是微软工程师们给我们的语法糖(PS:可能有点偏激)。委托与事件之间的关联可以参看下面的文章:

    C# 中的委托和事件-张子阳

      这里面要深究的其实应该是多播委托的委托链调用机制,拿出我们的反编译神器Reflecter,看看Combine里面有什么?

    1. Delegate.Combine里面调用了参数1的Delegate. CombineImpl方法,这一方法在普通的Delegate类中会抛出异常,而MulticastDelegate类重写了该方法。对该方法进行分析可以得出:将本实例及传入的实例这两个MulticastDelegate中的_invocationList列表组合,并以组合出的新列表创建一个新的MulticastDelegate返回。因此Combine返回的将是一个新的MulticastDelegate。_invocationList是一个List<Object>,这一点稍微有点奇怪,而且MulticastDelegate还提供了一个GetInvocationList方法返回Delegate数组,感觉有点多次一举,还得装箱拆箱。
    2. EventHandler继承自MulticastDelegate,并实现了Invoke,BeginInvoke等方法,但是很遗憾,方法内容都是CLR内部调用了,无法探究,但是从MulticastDelegate的构成大略可以猜测出,是遍历_invocationList,并逐个调用每个实例的Delegate.Invoke方法。

    4.使用事件不得不注意的事

      上面粗略描述了事件的使用,自定义及其深入机制,下面说说使用事件时需要注意的几个问题:

    1. 都碰到过”线程间操作无效”的异常吧,没错,即便在事件中改变界面控件的属性,调用方法等,也会碰到这个提示,但是有人会说,我在按钮事件中去改变lable的值,为什么没有异常呢?OK,还是委托的机制,委托其实是方法指针,调用委托就意味着委托的实际方法体运行在调用方一致的线程中,UI控件运行在什么线程下呢?界面线程,所以UI事件间进行操作是不会报异常的。如果你用一个SOCKET,如TCPClient,响应接受到数据的事件的话,那么这个事件将不会运行在界面主线程下,于是你就会看到”线程间操作无效”。
    2. 如果事件回调中有一个长时间执行的代码会如何呢?在按钮事件回调里写过数据库操作的人都有同感:卡……。这意味着事件是同步回调而不是异步回调,所以,把你的代码移开吧,并且仔细检查你的代码,保证回调函数中没有诸如死循环之类的……
    3. 如果关联了多个事件回调,那么他们的执行有顺序么?按照官方说法:不保证回调依照绑定顺序调用。不过实际经验来说大多数是和绑定顺序相同的,同时,一个阻塞了,后面的都不会执行;一个回调异常将影响所有回调。
  • 相关阅读:
    Qt 解析EXcel文件
    Qt PC 安卓 tcp传输文件
    Qt listwigwt item 加入自定义元素
    Qt 独立运行时伴随CMD命令窗口
    xml模块
    shelve模块
    json模块 pickle模块
    sys 模块
    os模块
    添加变量
  • 原文地址:https://www.cnblogs.com/hailan/p/3644734.html
Copyright © 2020-2023  润新知