public static voID Raise<T>(this EventHandler<T> handler,object sender,T e) where T : EventArgs{ if (handler != null) { handler(sender,e); }}
并提高它:
SomeEvent.Raise(this,new FooEventArgs());
一直在阅读Jon Skeet的articles on multithreading之一,我试图封装他主张通过扩展方法提出一个事件的方法(具有类似的通用版本):
public static voID Raise(this EventHandler handler,object @lock,EventArgs e){ EventHandler handlercopy; lock (@lock) { handlercopy = handler; } if (handlercopy != null) { handlercopy(sender,e); }}
这样可以这样调用:
protected virtual voID OnSomeEvent(EventArgs e){ this.someEvent.Raise(this.eventLock,this,e);}
这样做有什么问题吗?
此外,我有点混淆了锁的必要性在第一位.据了解,代理在文中的示例中被复制,以避免在null检查和委托调用之间改变(并变为null)的可能性.但是,我的印象是这种访问/转让是原子的,所以为什么锁是必需的?
更新:关于马克辛普森在下面的评论,我投了一个考验:
static class Program{ private static Action foo; private static Action bar; private static Action test; static voID Main(string[] args) { foo = () => Console.Writeline("Foo"); bar = () => Console.Writeline("bar"); test += foo; test += bar; test.test(); Console.ReadKey(true); } public static voID Test(this Action action) { action(); test -= foo; Console.Writeline(); action(); }}
输出:
FoobarFoobar
这表明方法(action)的delegate参数不会镜像传入它的参数(test),这是我预料的.我的问题是这会影响我在升级扩展方法的上下文中的锁的有效性吗?
更新:这是我正在使用的代码.它不像我想要的那样优雅,但似乎工作:
public static voID Raise<T>(this object sender,ref EventHandler<T> handler,object eventLock,T e) where T : EventArgs{ EventHandler<T> copy; lock (eventLock) { copy = handler; } if (copy != null) { copy(sender,e); }}解决方法 锁定的目的是在您覆盖默认事件线路时维护线程安全性.道歉,如果其中一些是解释你已经能够从乔恩的文章推断的事情;我只是想确保我完全清楚一切.
如果你这样声明你的活动:
public event EventHandler Click;
然后订阅该事件将自动与一个锁同步(这).您不需要编写任何特殊的锁定代码来调用事件处理程序.完全可以写:
var clickHandler = Click;if (clickHandler != null){ clickHandler(this,e);}
但是,如果您决定覆盖默认事件,即:
public event EventHandler Click{ add { click += value; } remove { click -= value; }}
现在你有一个问题,因为没有隐式的锁了.你的事件处理程序只是失去了线程安全性.这就是为什么你需要使用锁:
public event EventHandler Click{ add { lock (someLock) // normally generated as lock (this) { _click += value; } } remove { lock (someLock) { _click -= value; } }}
就我个人而言,我不打扰这个,但乔恩的理由是健全的.但是,我们确实有一个小问题.如果您正在使用一个专用的EventHandler字段来存储事件,那么您可能会在类的内部部署代码:
protected virtual voID OnClick(EventArgs e){ EventHandler handler = _click; if (handler != null) { handler(this,e); }}
这是坏的,因为我们正在访问相同的私有存储字段,而不使用该属性使用的相同的锁.
如果该类外部的一些代码:
MyControl.Click += MyClickHandler;
通过公共财产的外部代码正在履行锁定.但是你不是,因为你正在触摸私人领域.
clickHandler = _click的变量赋值部分是原子的,是的,但是在该赋值过程中,_click字段可能处于一个暂时状态,一个外部类被半写.当您同步访问某个字段时,只能同步写访问是不够的,您还必须同步读取访问权限:
protected virtual voID OnClick(EventArgs e){ EventHandler handler; lock (someLock) { handler = _click; } if (handler != null) { handler(this,e); }}
UPDATE
事实证明,OP的更新证明了围绕评论的一些对话实际上是正确的.这不是扩展方法本身的一个问题,事实上代理具有值类型语义并在赋值时被复制.即使你把这个从扩展方法中拿出来,只是调用它作为一个静态方法,你会得到相同的行为.
尽管我很确定您不能使用扩展方法,但可以使用静态实用程序方法来解决此限制(或特性,具体取决于您的观点).这是一个静态的方法,将工作:
public static voID RaiseEvent(ref EventHandler handler,object sync,EventArgs e){ EventHandler handlercopy; lock (sync) { handlercopy = handler; } if (handlercopy != null) { handlercopy(sender,e); }}
这个版本的工作原理是因为我们实际上并没有传递EventHandler,只是引用它(注意方法签名中的引用).不幸的是,您不能在扩展方法中使用ref,因此它必须保持纯静态方法.
(并且如前所述,您必须确保您传递与您在公共事件中使用的sync参数相同的锁定对象;如果传递任何其他对象,那么整个讨论是无效的.)
总结以上是内存溢出为你收集整理的这是C#中提升事件的有效模式吗?全部内容,希望文章能够帮你解决这是C#中提升事件的有效模式吗?所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)