当前位置:  开发笔记 > 编程语言 > 正文

使用扩展方法提升C#事件 - 这很糟糕吗?

如何解决《使用扩展方法提升C#事件-这很糟糕吗?》经验,为你挑选了3个好方法。

我们都熟悉C#事件声明的恐怖.为了确保线程安全,标准是写这样的东西:

public event EventHandler SomethingHappened;
protected virtual void OnSomethingHappened(EventArgs e)
{            
    var handler = SomethingHappened;
    if (handler != null)
        handler(this, e);
}

最近在这个板上的一些其他问题(我现在找不到),有人指出在这种情况下可以很好地使用扩展方法.这是一种方法:

static public class EventExtensions
{
    static public void RaiseEvent(this EventHandler @event, object sender, EventArgs e)
    {
        var handler = @event;
        if (handler != null)
            handler(sender, e);
    }
    static public void RaiseEvent(this EventHandler @event, object sender, T e)
        where T : EventArgs
    {
        var handler = @event;
        if (handler != null)
            handler(sender, e);
    }
}

有了这些扩展方法,你需要声明和引发一个事件就像这样:

public event EventHandler SomethingHappened;

void SomeMethod()
{
    this.SomethingHappened.RaiseEvent(this, EventArgs.Empty);
}

我的问题:这是个好主意吗?没有标准的On方法,我们是否遗漏了什么?(我注意到的一件事是它不适用于具有显式添加/删除代码的事件.)



1> Jon Skeet..:

它仍然适用于具有显式添加/删除的事件 - 您只需要使用委托变量(或者您已经存储了委托)而不是事件名称.

但是,有一种更简单的方法可以使其成为线程安全的 - 使用no-op处理程序初始化它:

public event EventHandler SomethingHappened = delegate {};

调用额外委托的性能可以忽略不计,它确实使代码更容易.

顺便说一句,在您的扩展方法中,您不需要额外的局部变量 - 您可以这样做:

static public void RaiseEvent(this EventHandler @event, object sender, EventArgs e)
{
    if (@event != null)
        @event(sender, e);
}

static public void RaiseEvent(this EventHandler @event, object sender, T e)
    where T : EventArgs
{
    if (@event != null)
        @event(sender, e);
}

我个人不会使用关键字作为参数名称,但它根本没有真正改变调用方面,所以做你想要的:)

编辑:至于"OnXXX"方法:你是否计划派生你的课程?在我看来,大多数课程都应该密封.如果这样做,您是否希望这些派生类能够引发事件?如果这些问题中的任何一个的答案都是"不",那就不要打扰了.如果两者的答案都是"是",那么:)


@Mike:不,我不相信JITter*被允许这样做.这是一段时间以前提出的问题,但我已经和某人说过了(我不记得是Joe Duffy还是Vance Morrison;有人这样),他说这完全违反了JITter所允许的规则和记忆模型.
你们需要将[MethodImpl(MethodImplOptions.NoInlining)]属性添加到这些扩展方法中,否则你可以通过JITter优化将代理复制到临时变量的尝试,从而允许空引用异常.这可能发生在Kyralessa和Jon Skeet的版本中.所以只需添加[MethodImpl(MethodImplOptions.NoInlining)]就可以了,是的,您不再需要显式复制到临时变量,因为只需通过方法参数传入值就可以实现这一点.
@Ghopper21:不,在这种情况下,参数充当局部变量,实际上 - 事件处理程序的任何机会都不会在`RaiseEvent`中可见,因为`@ event`只是一个参数...一个正常的局部变量.调用该方法时,该字段的当前值将被复制为参数的值...并且对该字段的进一步更改不会影响它.

2> Bob Sammers..:

现在C#6在这里,有一种更紧凑,线程安全的方式来触发事件:

SomethingHappened?.Invoke(this, e);

Invoke() 只有委托者为事件注册时才会被调用(即它不为空),这要归功于空条件运算符"?".

问题所解决的"处理程序"代码的线程问题在这里被回避,因为,就像在该代码中一样,SomethingHappened只访问一次,因此在测试和调用之间不可能将其设置为null.

这个答案可能与原始问题相关,但对那些寻找更简单方法来提高事件的人来说非常相关.



3> Robert Pauls..:

[这是一个想法]

只需以推荐的方式编写代码一次并完成它.那么你不会混淆你的同事看着代码,认为你做错了什么?

[我阅读了更多关于编写事件处理程序的方法,而不是编写事件处理程序.]

推荐阅读
wurtjq
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有