这对我来说似乎很奇怪 - VB.NET通过其RaiseEvent关键字隐式处理空检查.它似乎大大增加了围绕事件的样板数量,我看不出它提供了什么好处.
我确信语言设计师有充分的理由这样做..但我很好奇,如果有人知道为什么.
Jon*_*eet 18
这当然是一个烦恼的问题.
当你编写一个访问类中类字段事件的代码时,你实际上是在访问字段本身(在C#4中以模数进行一些修改;暂时不要去那里).
所以,选项将是:
以不同方式处理所有委托调用,以便:
Action<string> x = null;
x();
Run Code Online (Sandbox Code Playgroud)
不会抛出异常.
当然,对于非void委托(和事件),这两个选项都会引发一个问题:
Func<int> x = null;
int y = x();
Run Code Online (Sandbox Code Playgroud)
应该默默地返回0吗?(默认值为int.)或者它实际上是掩盖了一个错误(更有可能).让它静静地忽略你试图调用null委托的事实会有些不一致.在这种情况下甚至更奇怪,它不使用C#的语法糖:
Func<int> x = null;
int y = x.Invoke();
Run Code Online (Sandbox Code Playgroud)
基本上,无论你做什么,事情都变得棘手并且与语言的其他部分不一致.我也不喜欢它,但我不确定什么是实用但一致的解决方案......
pli*_*nth 13
我们通常通过声明我们的事件来解决这个问题:
public event EventHandler<FooEventArgs> Foo = delegate { };
Run Code Online (Sandbox Code Playgroud)
这有两个好处.首先是我们没有检查null.第二,我们避免了典型事件触发中无所不在的关键部分问题:
// old, busted code that happens to work most of the time since
// a lot of code never unsubscribes from events
protected virtual void OnFoo(FooEventArgs e)
{
// two threads, first checks for null and succeeds and enters
if (Foo != null) {
// second thread removes event handler now, leaving Foo = null
// first thread resumes and crashes.
Foo(this, e);
}
}
// proper thread-safe code
protected virtual void OnFoo(FooEventArgs e)
{
EventHandler<FooEventArgs> handler = Foo;
if (handler != null)
handler(this, e);
}
Run Code Online (Sandbox Code Playgroud)
但是,通过将Foo自动初始化为空委托,从不需要任何检查,代码自动是线程安全的,并且更容易读取引导:
protected virtual void OnFoo(FooEventArgs e)
{
Foo(this, e); // always good
}
Run Code Online (Sandbox Code Playgroud)
在空手道小子向Pat Morita道歉,"避免null的最佳方法就是没有."
至于为什么,C#并没有像VB那样溺爱你.尽管event关键字隐藏了多播委托的大部分实现细节,但它确实提供了比VB更精细的控制.
如果设置管道以首先引发事件将是昂贵的(如SystemEvents),或者在准备事件参数时将是昂贵的(如Paint事件),您需要考虑需要什么代码.
事件处理的Visual Basic风格不允许您推迟支持此类事件的成本.您无法覆盖添加/删除访问器以延迟将昂贵的管道放置到位.并且您无法发现可能没有订阅任何事件处理程序,因此刻录循环以准备事件参数是浪费时间.
在C#中不是问题.方便和控制之间的经典权衡.
扩展方法提供了一种非常酷的方式来解决这个问题.请考虑以下代码:
static public class Extensions
{
public static void Raise(this EventHandler handler, object sender)
{
Raise(handler, sender, EventArgs.Empty);
}
public static void Raise(this EventHandler handler, object sender, EventArgs args)
{
if (handler != null) handler(sender, args);
}
public static void Raise<T>(this EventHandler<T> handler, object sender, T args)
where T : EventArgs
{
if (handler != null) handler(sender, args);
}
}
Run Code Online (Sandbox Code Playgroud)
现在你可以简单地这样做:
class Test
{
public event EventHandler SomeEvent;
public void DoSomething()
{
SomeEvent.Raise(this);
}
}
Run Code Online (Sandbox Code Playgroud)
但是正如其他人已经提到的那样,您应该了解多线程场景中可能存在的竞争条件.