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

推荐订阅源

博客园_首页
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
P
Proofpoint News Feed
G
Google Developers Blog
B
Blog
Engineering at Meta
Engineering at Meta
阮一峰的网络日志
阮一峰的网络日志
The Register - Security
The Register - Security
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
博客园 - 叶小钗
The Cloudflare Blog
The Hacker News
The Hacker News
D
Darknet – Hacking Tools, Hacker News & Cyber Security
C
CXSECURITY Database RSS Feed - CXSecurity.com
雷峰网
雷峰网
F
Fortinet All Blogs
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
H
Hackread – Cybersecurity News, Data Breaches, AI and More
酷 壳 – CoolShell
酷 壳 – CoolShell
Last Week in AI
Last Week in AI
T
Threat Research - Cisco Blogs
A
About on SuperTechFans
量子位
Recorded Future
Recorded Future
博客园 - 三生石上(FineUI控件)
H
Help Net Security
Help Net Security
Help Net Security
P
Palo Alto Networks Blog
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
T
Troy Hunt's Blog
W
WeLiveSecurity
V
Vulnerabilities – Threatpost
T
The Exploit Database - CXSecurity.com
Know Your Adversary
Know Your Adversary
Apple Machine Learning Research
Apple Machine Learning Research
Scott Helme
Scott Helme
N
News | PayPal Newsroom
AWS News Blog
AWS News Blog
D
DataBreaches.Net
Blog — PlanetScale
Blog — PlanetScale
MongoDB | Blog
MongoDB | Blog
B
Blog RSS Feed
腾讯CDC
J
Java Code Geeks
Microsoft Azure Blog
Microsoft Azure Blog
TaoSecurity Blog
TaoSecurity Blog
GbyAI
GbyAI
Y
Y Combinator Blog
Hacker News - Newest:
Hacker News - Newest: "LLM"
D
Docker

博客园 - 大约在冬季

如何在IronPython中使用C#扩展方法 LINQ入门教程示例使用F#的实现 宝宝照片更新喽 再谈两种不同字符串比较方法的性能对比 - 大约在冬季 - 博客园 如何让DevExpress.TreeList单元格中的自定义控件包含标签 见证中国A股市场:上午大盘加速寻底 沪指跌129点,探低至4241.02 漂亮宝宝100天啦!庆祝一下! F# 学习笔记(1/n) 权重股杀跌沪指半日破两关跌135点 IronPyton分析表达式 WCF服务契约 听课笔记 最近性能优化一些感触,分享中…… 两种不同字符串比较方法的性能对比 设计时支持:如何获取环境数据 如何在C#中调用 IronPython 代码 (基于IronPython 2.0A3) 运行Oracle数据库配置向导创建数据库失败ORA-24324的解决方案 C#3.0 自动属性——只能在简单属性上偷懒 性能——换个角度看问题 设计模式的滋味
.Net 事件类型的实现和推荐做法
大约在冬季 · 2007-08-15 · via 博客园 - 大约在冬季

首先来看一下我们常见的自定义事件实现方式,首先创建可能的事件参数

 1 /// <summary>
 2     /// 事件参数
 3     /// </summary>
 4     public sealed class DemoEventArgs:EventArgs {
 5 
 6         /// <summary>
 7         /// 获取或设置事件的上下文
 8         /// </summary>
 9         public string Context {
10             get { return context; }
11             set { context = value; }
12         }
13         private string context;
14 
15 
16     }

当然,如果在实际使用中不需要事件参数的话,可以直接使用EventArgs.Empty静态字段。
紧接着我们需要定义事件成员

1/// <summary>
2        /// 事件成员
3        /// </summary>

4        public event EventHandler<DemoEventArgs> NewEv

然后,我们就需要引发这个事件

 1 public void InvokeNewEvent() {
 2             DemoEventArgs e = new DemoEventArgs();
 3             e.Context = "Hello world";
 4             this.OnNewEvent(e);
 5         }
 6 
 7         protected virtual void OnNewEvent(DemoEventArgs e) {
 8             if (NewEvent!=null) {
 9                 NewEvent(this, e);
10             }
11         }

对于上面的代码,我们通过Reflector工具可以看到,对于事件定义

public event EventHandler<DemoEventArgs> NewEvent;

编译程序会将其翻译成两个public方法。

相应代码如下

 1[MethodImpl(MethodImplOptions.Synchronized)]
 2public void add_NewEvent(EventHandler<DemoEventArgs> value)
 3{
 4    this.NewEvent = (EventHandler<DemoEventArgs>) Delegate.Combine(this.NewEvent, value);
 5}

 6
 7[MethodImpl(MethodImplOptions.Synchronized)]
 8public void remove_NewEvent(EventHandler<DemoEventArgs> value)
 9{
10    this.NewEvent = (EventHandler<DemoEventArgs>) Delegate.Remove(this.NewEvent, value);
11}

12
13 
14
15 
16

MethodImpOption枚举类型定义了方法是如何被执行的。Synchronized指定了同时只能由一个线程执行该方法。静态方法锁定类型,而实例方法锁定实例。这样做的目的是,保证在操作实例事件时,对于每一个对象,在同一时刻add和remove方法的线程安全。
在类型上,所有事件的add和remove方法都将使用相同的锁。这样造成,在多个线程同时对不同事件进行订阅和撤销的时候,就会出现性能损失。在MSDN上,我们可以查阅到最后由一个注意事项:“实例或类型上的锁定(如同使用 Synchronized 标志一样)对于公共类型是不推荐使用的,其原因在于除了不是自己的代码的其他代码可对公共类型和实例采用锁定。这可能导致死锁或其他同步问题。 ”
线程同步的指导方针是不应该在对象本身上加同步锁,因为同步锁将对所有的代码公开,这意味着任何人都有可能蓄意的编写代码Lock这个对象,造成其它线程死锁。

为此,在大多数情况下上述情况并不可能发生,但是对于一个完美而稳固的组件来说,这就显得相当重要。鉴于以上原因,我们重新定义事件的实现。

 1 public class EventDemo {
 2        /// <summary>
 3        /// 私有同步锁
 4        /// </summary>

 5        private readonly object _eventLock = new object(); 
 6
 7        /// <summary>
 8        /// 事件成员
 9        /// </summary>

10        private event EventHandler<DemoEventArgs> internalNewEvent;
11        public event EventHandler<DemoEventArgs> NewEvent {
12            add {
13                lock (_eventLock) {
14                    internalNewEvent += value;
15                }

16            }

17            remove {
18                lock (_eventLock) {
19                    internalNewEvent -= value;
20                }

21            }

22        }

23
24        protected virtual void OnNewEvent(DemoEventArgs e) {
25            //出于线程考虑,委托字段保存到临时字段中
26            EventHandler<DemoEventArgs> t = internalNewEvent;
27            if (t!=null{
28                internalNewEvent(this, e);
29            }

30        }

31
32        public void InvokeNewEvent() {
33            DemoEventArgs e = new DemoEventArgs();
34            e.Context = "Hello world";
35            this.OnNewEvent(e);
36        }

37
38        
39
40    }

再次查看编译程序最终生成的运行时代码

 1public void add_NewEvent(EventHandler<DemoEventArgs> value)
 2{
 3    lock (this._eventLock)
 4    {
 5        this.internalNewEvent = (EventHandler<DemoEventArgs>) Delegate.Combine(this.internalNewEvent, value);
 6    }

 7}

 8
 9public void remove_NewEvent(EventHandler<DemoEventArgs> value)
10{
11    lock (this._eventLock)
12    {
13        this.internalNewEvent = (EventHandler<DemoEventArgs>) Delegate.Remove(this.internalNewEvent, value);
14    }

15}

16
17 
18
19 
20

从性能上考虑,当某个组件定义大量事件时,该实例会浪费大量的内存,之所以是浪费,是因为其中很多事件都没有被订阅。这一点,我们可以参考System.Web.UI.Control中的实现。首先在内部包含了一个EventHandlerList类型的Events属性。

 protected EventHandlerList Events
    
{
        
get
        
{
            
this.EnsureOccasionalFields();
            
if (this._occasionalFields.Events == null)
            
{
                
this._occasionalFields.Events = new EventHandlerList();
            }

            
return this._occasionalFields.Events;
        }

    }

 public event EventHandler DataBinding
    
{
        add
        
{
            
this.Events.AddHandler(EventDataBinding, value);
        }

        remove
        
{
            
this.Events.RemoveHandler(EventDataBinding, value);
        }

    }

引发事件

 protected virtual void OnDataBinding(EventArgs e)
    
{
        
if (this.HasEvents())
        
{
            EventHandler handler 
= this._occasionalFields.Events[EventDataBinding] as EventHandler;
            
if (handler != null)
            
{
                handler(
this, e);
            }

        }

    }

这样实现自我感觉不是很好,由于EventHandlerList是一个链表,造成通过内部的find方法查找时,要遍历循环。更理想的做法是使用一个Hashtable来存储委托链。
(参考:CLR via C#)