惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Engineering at Meta
Engineering at Meta
T
Threat Research - Cisco Blogs
V
Vulnerabilities – Threatpost
T
Tor Project blog
T
Troy Hunt's Blog
C
CERT Recently Published Vulnerability Notes
C
Cisco Blogs
W
WeLiveSecurity
Cloudbric
Cloudbric
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
爱范儿
爱范儿
Google Online Security Blog
Google Online Security Blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Simon Willison's Weblog
Simon Willison's Weblog
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
Martin Fowler
Martin Fowler
Cisco Talos Blog
Cisco Talos Blog
F
Full Disclosure
MongoDB | Blog
MongoDB | Blog
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
I
Intezer
www.infosecurity-magazine.com
www.infosecurity-magazine.com
G
GRAHAM CLULEY
B
Blog RSS Feed
云风的 BLOG
云风的 BLOG
人人都是产品经理
人人都是产品经理
M
MIT News - Artificial intelligence
腾讯CDC
L
LangChain Blog
L
LINUX DO - 热门话题
H
Help Net Security
S
Schneier on Security
N
Netflix TechBlog - Medium
博客园 - Franky
酷 壳 – CoolShell
酷 壳 – CoolShell
Spread Privacy
Spread Privacy
S
Secure Thoughts
T
The Exploit Database - CXSecurity.com
P
Privacy International News Feed
P
Privacy & Cybersecurity Law Blog
Cyberwarzone
Cyberwarzone
A
About on SuperTechFans
NISL@THU
NISL@THU
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
D
DataBreaches.Net
The GitHub Blog
The GitHub Blog
Recorded Future
Recorded Future
雷峰网
雷峰网
AWS News Blog
AWS News Blog
V2EX - 技术
V2EX - 技术

博客园 - MHL

SSRS 2016 Forms Authentication 存储配置关系&知识图谱 Neo4j 使用cypher语言进行查询 项目实战--知识图谱初探 - MHL - 博客园 .NET Core多语言 ASP.NET Core WebApi 返回统一格式参数 C#启动外部程序以及等待外部程序关闭的几种方法 开源.net 混淆器ConfuserEx介绍 CRUD Operations In ASP.NET MVC 5 Using ADO.NET asp.net mvc 利用过滤器进行网站Meta设置 【译】RAID的概念和RAID对于SQL性能的影响 【转】Sql server锁,独占锁,共享锁,更新锁,乐观锁,悲观锁 One Day WinForm简单进度条 金庸群侠传 3小时爆机 ExtJs Set PropertyGrid Column Name ExtJs GridPanel 生成列 电脑上玩 Google纵横 Microsoft Visual Studio 2010 宣传短片
C#:类的成员--事件
MHL · 2021-08-31 · via 博客园 - MHL

鸿门宴上项羽摔杯为号,埋伏两侧的刀斧手就会杀将而来,将刘邦一干人等就地伏法。这就是一个事件模型。

事件模型:事件拥有者、事件、事件响应者、响应事件的处理方法、事件订阅;当某种情况发生时,触发事件。

  • 以鸿门宴摔杯为号的故事为例:
    1. 事件拥有者:项羽
    2. 事件:摔杯子
    3. 事件参数(重要但非必须):谁摔了杯子(对于杀手来说,管你3721,听过杯子摔了就是干。)
    4. 事件响应者:刀斧手
    5. 事件订阅:项羽对刀斧手说:“明日设宴,孤以摔杯为号,尔等即刻行事!”
    6. 事件触发:项羽看劝降不成,勃然大怒,抓起杯子摔倒地上。(事件发生是需要触发的,否则事件永远不会发生,事件响应方法也永远不会执行。)

下面通过自定义事件,来模拟摔杯为号的事件模型:

声明事件的第一种方式

class Program
{
    static void Main(string[] args)
    {
        XiangYu zhugong = new XiangYu();
        ShaShou daofushou = new DaoFuShou();
        ShaShou gongjianshou = new GongjianShou();

        //6 杀手订阅主公的摔杯事件
        zhugong.ShuaiBeiziEvent += new EventHandler<BeiziEventArgs>(daofushou.Action);
        zhugong.ShuaiBeiziEvent += gongjianshou.Action;

        //7 触发事件--- 谈判中(项羽可能随时会摔杯子)
        zhugong.Quanxiang();

        Console.ReadKey();
    }
}

//1 事件携带的附加信息
class BeiziEventArgs : EventArgs
{
    //杯子材质
    public string Metarials { get; set; }

    public BeiziEventArgs(string meterials)
    {
        this.Metarials = meterials;
    }
}

//2 定义事件拥有者
class XiangYu
{
    private EventHandler<BeiziEventArgs> eventHandler;

    //3 定义摔杯事件
    public event EventHandler<BeiziEventArgs> ShuaiBeiziEvent
    {
        add
        {
            eventHandler += value;
        }

        remove
        {
            eventHandler -= value;
        }
    }

    public void Quanxiang()
    {
        var ss = "你可以按下s来触发摔杯事件";
        Console.WriteLine(ss);
        do
        {
            Console.WriteLine("宴会进行中...");
            ss = Console.ReadLine();
        } while ("s"!=ss);
        Console.WriteLine("项羽怒了 抄起酒杯摔了下去");
        if (this.eventHandler!=null)
        {
            BeiziEventArgs attachinfo = new BeiziEventArgs("葡萄美酒夜光杯");
            eventHandler.Invoke(this, attachinfo);
        }
    }
}

//4 定义事件响应者
abstract class ShaShou
{
    //5 在响应者中 定义事件处理方法
    public abstract void Action(object sender, BeiziEventArgs e);
}

class DaoFuShou : ShaShou
{
    //刀斧手
    public override void Action(object sender, BeiziEventArgs e)
    {
        Console.WriteLine($"{e.Metarials}被摔了,进去拿人");
    }
}

class GongjianShou : ShaShou
{
    //弓箭手
    public override void Action(object sender, BeiziEventArgs e)
    {
        Console.WriteLine($"{e.Metarials}被摔了,准备放箭");
    }
}

运行结果:
摔杯事件
代码分析:

  • 这是一个标准的 事件位于事件拥有者中、事件处理方法位于事件响应者中的实现方式:
    简略关系
  • 从事件的声明方式上可以看出,事件是类的一个成员;不要把事件误认为是一种类型;
  • 第六步订阅事件的时候,可以使用委托实例、也可以使用方法名,这是一个语法糖;

声明事件的第二种方式:使用事件声明语法糖,来完成上面的功能,在XiangYu类型改动如下:

class XiangYu
{
    //3 定义摔杯事件
    public event EventHandler<BeiziEventArgs> ShuaiBeiziEvent;

    public void Quanxiang()
    {
        var ss = "你可以按下s来触发摔杯事件";
        Console.WriteLine(ss);
        do
        {
            Console.WriteLine("宴会进行中...");
            ss = Console.ReadLine();
        } while ("s"!=ss);
        Console.WriteLine("项羽怒了 抄起酒杯摔了下去");
        if (this.ShuaiBeiziEvent!=null)
        {
            BeiziEventArgs attachinfo = new BeiziEventArgs("葡萄美酒夜光杯");
            ShuaiBeiziEvent.Invoke(this, attachinfo);
        }
    }
}

为什么说 public event EventHandler ShuaiBeiziEvent; 是一种语法糖,我们通过反编译工具可以看出:编译器看到这句代码时,为我们声明了一个私有的委托字段、一个add_xxx方法追加一个委托、一个remove_xxx方法移除一个委托
事件语法糖
其实从反编译出来的代码可以看出,这两个方法的详细实现:
add
remove
分析以上内容后我们发现,事件声明语法糖,是编译器以一种安全的方式为我们隐去了第一种方式的步骤,所以我们推荐第二种方式,但要知其然知其所以然;另外,事件是委托的包装器。他包装的这个委托内部维护了一个委托链。
内部实现

下面是"摔杯为号"故事的详细关系图:

详细实现
本篇中并没有定义一种新的委托类型,而是使用了.NET为我们提供的EventHanler委托类型,为的是不做额外的工作(也不建议无脑定义委托类型,因为.NET为我们提供的委托类型大部分都够用了);若真的需要定义一种委托类型来约束事件处理方法的话,委托类型名要以EventHadnler结尾,如:ClickEventHandler;事件模式更多是用于客户端程序中的,因为他们是基于事件驱动的;事件用的多,而自己定义的情况很少,但是在我们使用事件前了解清楚事件的原理是十分重要的。

如本文的标题一样:事件是类的成员;类的三大成员:属性、方法、事件。以上便是对事件的总结,记录下来以便以后查阅。