C# 事件处理程序的适当用法

本文关键字:用法 事件处理 程序 | 更新日期: 2023-09-27 18:30:23

我目前正在构建一个 C# 应用程序,该应用程序将在用户连接到特定无线网络时根据某些网络资源自动对用户进行身份验证。

目前,我正在使用托管 Wifi API 来发现用户何时连接/断开无线网络。我有一个事件处理程序,因此当发生任何这些活动时,将调用我的一个方法来检查无线连接的当前状态。

为了管理应用程序的状态,我有另一个称为"导体"的类,它执行更改应用程序状态所需的操作。例如,当无线网卡连接到正确的网络时,导体需要将系统状态从"监控"更改为"身份验证"。如果身份验证成功,则导体需要将状态更改为"已连接"。断开连接会导致再次处于"监视"状态,并且身份验证错误会导致"错误"状态。这些状态更改(如果用户请求)可能会导致 TrayIcon 通知,因此用户知道他们正在进行身份验证。

我目前的想法涉及让用于检查无线当前状态的方法调用状态管理器中的"身份验证"或"断开连接"方法。但是,我不确定这是否是事件处理程序的适当用法 - 它应该设置标志或通过某种形式的IPC将消息发送到将开始身份验证/断开连接过程的单独线程吗?

除了事件处理程序能够请求连接/断开连接外,用户还可以通过托盘图标执行它。因此,我需要确保这些后台操作不会阻止托盘与用户的交互。

只有一个组件应该能够随时请求更改系统状态,因此我需要使用互斥锁来防止并发状态更改。但是,我应该如何同步其余的这些组件对我来说是一个小小的谜。

我应该阅读的任何建议或文献都会受到赞赏。我没有接受过 C# 语言的正式培训,所以如果我陈述了什么,我深表歉意。

编辑:最重要的是,我想验证事件将作为单独的线程执行,因此它无法阻止主UI。此外,我想验证如果我有一个事件处理程序订阅了一个事件,它将串行处理事件,而不是并行处理事件(因此,如果用户在处理第一个连接事件之前连接和断开连接,则不会同时发生两个状态更改)。

C# 事件处理程序的适当用法

我应该阅读的任何建议或文献都会受到赞赏。我没有接受过 C# 语言的正式培训,所以如果我陈述了什么,我深表歉意。

这解释了一些事情。 :)
我会阅读线程、事件处理和系统托盘图标/界面的创建。

请务必注意以下几点:

  • 事件在调用它们的同一线程上处理。 如果您希望事件的处理不锁定 GUI,则需要让按钮将工作移动到其他线程。
  • 触发事件时,它会将相应的参数传递给其列表中的所有方法。 这与调用一个方法几乎相同,而该方法又调用所有其他方法(参见 EventFired 示例)。 事件的目的不是调用方法,因为我们已经可以这样做了,而是调用编译代码时可能不知道的方法(例如,当编译控件所在的库时,按钮控件上的单击事件将未知)。 简而言之,如果可以调用该方法而不是使用事件,则这样做。

    void EventFired(int arg1, object arg2)
    {
        subscribedMethod1(arg1, arg2);
        SubscribedMethod2(arg1, arg2);
        SubscribedMethod3(arg1, arg2);
        SubscribedMethod4(arg1, arg2);
        SubscribedMethod5(arg1, arg2);
        SubscribedMethod6(arg1, arg2);
        SubscribedMethod7(arg1, arg2);
    }
    
  • 如果要防止用户界面锁定,请在另一个线程上执行该工作。 但请记住,用户界面元素(表单、按钮、网格、标签等)只能从其主机线程访问。 使用该控件。调用方法以在其线程上调用方法。

  • 从界面中删除选项不是防止赛道条件的好方法(用户在已经运行时启动连接/断开连接),因为用户界面将位于不同的线程上并且可能不同步(单独的线程同步需要时间)。 虽然有很多方法可以解决此问题,但对于刚接触线程的人来说,最简单的方法是对值使用锁。 这样,.NET 将确保一次只有一个线程可以更改设置。 您仍然需要更新用户界面,以便用户知道正在发生更新。

您的总体设计听起来不错。 您可以使用 2-3 个线程(1 个用于用户界面(托盘图标),1 个用于检查新的网络连接,1 个(可以与连接检查合并)用于检查互联网连接。

希望这有所帮助,如果您需要更多(或接受答案),请告诉我们。

作为一种选择,替代...

如果我是你,既然你无论如何都要重新开始,我会认真考虑
Rx 反应式扩展

它提供了对事件和基于事件的编程的全新外观,并且对您正在处理的事情(包括同步,处理线程,组合事件,停止,启动等)有很大帮助。

一开始学习可能

有点"陡峭",但同样,这可能是值得的。

希望这有帮助,

对我来说,你似乎要过度设计这个项目。您基本上需要在Commander中实现一个事件,并在主应用程序中订阅它们。那是。

如果总是有一个组件可以进行更改,而您可以拥有多个组件,那么使用某种同步机制(例如您注意到的Mutex)是完全有效的选择。

希望这有帮助。

如果您希望在任何时候最多挂起一个状态更改,最好让您正在侦听的外部事件的事件处理程序在执行期间保持锁定。这确保了编程的简单方法,因为可以保证应用的状态不会在您下方更改。在这种特殊情况下,不需要单独的线程。

您需要区分应用程序的当前状态和目标状态。用户指示目标状态("已连接"、"已断开连接")。实际状态可能有所不同。示例:用户希望断开连接,但实际状态为身份验证。身份验证步骤完成后,状态机必须检查目标状态:

targetState == connected => set current state to connected
targetState == disconnected => begin to disconnect and set state to disconnecting

分离实际状态和目标状态允许用户随时改变主意,状态机可以转向所需的状态。

如果不看到应用的整个(建议的)结构,就很难给出准确的答案。但总的来说,是的,使用事件处理程序来处理这类事情是可以的 - 尽管我可能会将实际实现移到单独的方法中,以便您可以更轻松地从其他位置触发它。

关于禁用"连接"按钮的评论对我来说听起来很合适,尽管可以想象您可能还需要其他形式的同步。但是,如果您的应用程序不需要多线程,我会避免仅仅为了它而引入多个线程。如果这样做,请查看已作为任务并行库的一部分包含的新任务 API。他们把很多东西抽象得相当好。

关于不要过度思考这个问题的评论也很好。如果我站在你的立场上,刚开始使用一门新语言,我会避免在一开始就试图让架构恰到好处。深入了解,并使用您已有的认知工具集进行开发。随着你探索的更多,你会发现,"哦,废话,这是一个更好的方法。然后去这样做。重构是你的朋友。