如何理解事件以及使用事件

**疑惑点:

**对于刚接触事件来说,往往会有一种现象那就是,我为什么要用事件来调用过程,我直接调用不行吗?一样可以达到效果啊,我根本就搞不懂为啥需要事件呢?

理解直接调用和事件调用的区别

首先引用MSDN对事件的描述
执行流程是由外部发生的事情(称为“事件”)决定的
你比如说,我的按钮单击了,但是我单击后的事情由用户处理而不是我在按钮内部处理,你没理由说,我单击了按钮,我又在按钮里面自己做了一件事,那么,这是不是意味着,我的按钮单击只能做这件事了?由外部决定意味着我按钮单击了可以做不同的事情,比如你单击可以改变颜色,单击可以修改文字等等。这是由你用户写的事件处理程序决定的而不是我事先写好的
写个代码示例,给大家看看直接调用和事件触发回调的区别。
这里做了一个登陆验证,假如成功就让BC类做事。这样很容易理解是吧,按顺序来的执行。可是这样却会增加类A对类BC的依赖,假如哪天类BC的方法要修改参数,是不是又得修改类A了?而且这个仅仅只是登录成功后类BC干成功的事,那么失败的呢?也就是说这样写的类,不能适应变化,写死了,增加需求或者减少需求都得大动干戈。这是因为我们指明了要干的具体事,而事件不是这样的,事件仅仅只是通知的作用,通知外部,登录验证成功了,你该干嘛就干嘛。而不是,登录成功了,我要干你BC的事不干其他人的事了。
直接调用:

 Public Class A
        Private name As String = "admin"
        Private password As String = "1234"
        Public Sub Login(username As String, userpassword As String)
            If username = name AndAlso userpassword Then
                Dim c As New C
                Dim B As New B
                B.DoSomething()
                c.DoSomthing()

            End If

        End Sub
    End Class
    Public Class B
        Public Sub DoSomething()
            Console.WriteLine("登录成功B干活")
        End Sub
    End Class
    Public Class C
        Public Sub DoSomthing()
            Console.WriteLine("登录成功C干活")


        End Sub
    End Class
    Dim WithEvents Login As New A
    Dim testb As New B
    Dim testc As New C
    Sub Main()
        Login.Login("admin", "1234")
        Console.ReadKey()
    End Sub

事件调用:

 Public Class A
        Private name As String = "admin"
        Private password As String = "1234"
        Public Event LoginStatus(success As Boolean)
        Public Sub Login(username As String, userpassword As String)
            RaiseEvent LoginStatus(username = name AndAlso userpassword = password)
        End Sub
    End Class
    Public Class B
        Public Sub DoSomething()
            Console.WriteLine("登录成功B干活")
        End Sub
    End Class
    Public Class C
        Public Sub DoSomthing()
            Console.WriteLine("登录成功C干活")


        End Sub
    End Class
    Dim WithEvents Login As New A
    Dim testb As New B
    Dim testc As New C
    Sub Main()
        Login.Login("admin", "1234")
        Console.ReadKey()
    End Sub

    Private Sub Login_LoginStatus(success As Boolean) Handles Login.LoginStatus
        testb.DoSomething()
        testc.DoSomthing()

    End Sub
    '输出结果B做事情、C做事情
    

事件执行的过程

在事件代码里,我们用了一个登录状态的事件通知外部,登录成功与否。然后执行就看外部用户如何写,不取决于内部类对它的实现。你可以假设一个场景,你要写一个封装好的类或者程序集,给别人用,那么,你在类的内部有些操作需要别人参与,而这操作是别人写的,你没法得知,那么你怎么调用他这操作呢?这时候你直接调用是不是没法调用了?这时就用事件或委托调用,也就是回调别人的方法。
而回调别人的方法,我们获取函数地址,才能进行回调是吧,那么函数地址是肯定存在的吧(程序需要载入内存中执行),至于函数地址里的函数写了什么不需要关注,只需要得到它调用就是了,而得到它就得用到委托了,所以事件关联委托,为啥说关联呢,因为事件触发委托跟着执行,随之就调用对应的过程,而这里触发事件的称为事件的发送者,对应的过程称为事件的接收者也就是事件处理程序,这两个关联起来呢,用委托,而且是个多播委托,多播委托简单理解就是委托列表,事件触发后循环执行委托列表中的委托(广播作用),所以你可以addhandler多个委托也就是这个原因了,而且你可以测试,在任意一个事件处理程序中阻塞它会导致卡住不往下执行其他委托对应的过程,所以他不是并发执行的它是一个循环中同步执行的。
总结一下:事件触发----广播委托----执行事件处理程序。就是这么简单

关于系统事件的声明

我去反编译看了一下源码,声明Click事件的时候用的就是
Public Custom Event xx As EventHandler 而这里的EventHandler是多播委托类型的。委托知识理解请看委托那篇文章。
这它用法。

  Public Custom Event TestEvent As EventHandler
        AddHandler(value As EventHandler)
            '此处在添加委托时执行,也就是使用Addhandler TestEvent,委托  时执行
        End AddHandler

        RemoveHandler(value As EventHandler)
            '此处在移除委托时执行 , ReMovehandler TestEvent ,委托  执行
        End RemoveHandler

        RaiseEvent(sender As Object, e As System.EventArgs)
            '此处在触发事件时执行, 使用语句raiseEvent TestEvent时执行
        End RaiseEvent
    End Event

为何不用委托直接执行呢?回调当然可以用委托执行啊,主要是用起来便捷不?且看下方

多播委托模拟事件调用

 Public Class A
        Private name As String = "admin"
        Private password As String = "1234"
        Public DoWork As Action
        Public Sub Login(username As String, userpassword As String)
            If DoWork IsNot Nothing Then
                If username = name AndAlso userpassword Then
                '下面类似于触发事件后干的活
                    For Each item In DoWork.GetInvocationList
                        item.DynamicInvoke(Nothing) 
                    Next
                End If

            End If
        End Sub
    End Class
    Public Class B
        Public Sub DoSomething()
            Console.WriteLine("登录成功B干活")
        End Sub
    End Class
    Public Class C
        Public Sub DoSomthing()
            Console.WriteLine("登录成功C干活")


        End Sub
    End Class
  
    Dim WithEvents Login As New A
    Dim testb As New B
    Dim testc As New C
    Sub Main()
        Login.DoWork = [Delegate].Combine(New Action(AddressOf testb.DoSomething), New Action(AddressOf testc.DoSomthing)) '此处可以继续combine多个


        Login.Login("admin", "1234")

        Console.ReadKey()
    End Sub
  

上面就是模拟的事件,用起来是不是相当麻烦?所以事件把上面的过程都封装好了,我们只需要触发事件,增加和减少委托,还能用withevents关键字配合handle添加委托,这样就能得到多个过程的回调,管理起来很方便。

感想

不了解本质很难去真正理解这东西。再多的比喻也是别人为了让你知道这是个什么玩意,你明白了他是什么,但是你却不明白为什么要用它,所以,了解本质去了解执行过程,以及比较用它和不用它的区别,你才能真正的理解为什么要用它,再多的比喻也接近不了本质。再往深层的来说,这就是一个内存操作的东西,所有的封装只是为了让使用更便捷,相应的也懵逼很多人,不知其所以然。
再者了,上面的直接调用过程,这个那么糟糕的用法,一般都用依赖注入进行的,然后你会说,依赖注入是个什么玩意?很高端的名称,实际上很简单,就是构造函数里加个NEW(byval xx类),这就是依赖XX类注入到类。然后不还是内存吗,哈哈,Byval 获取引用类型的地址,然后再获取方法执行。

也不知道写了那么多字你们看的懂不,我反正感觉是挺乱的,哈哈,事件确实比委托难讲一点,因为事件比委托多了一层,得层层剥开。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值