0

考虑以下代码:

public ViewModel()
{
    PropertyChanged += new PropertyChangedEventHandler(ViewModel_PropertyChanged);
    PropertyChanged += ViewModel_PropertyChanged;
}

void ViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
    throw new NotImplementedException();
}

在 VisualStudio 中,如果您键入PropertyChanged +=,应用程序会提示您按 Tab 键以生成一个事件处理程序,该处理程序类似于我的构造函数的第一行。但是,像构造函数中的第二行一样编写它也是有效的(并且更简洁)。

这两行有不同的含义吗?如果不是,为什么 VisualStudio 喜欢生成前者?

4

1 回答 1

4

语句语法是相当主观的,你不能争论品味。但值得注意的是,这两种形式的语句都是 C# 中的语法糖。糖的长期问题是它会产生腐烂的牙齿。这种语法隐藏了真正发生的事情,并且永远让 C# 程序员陷入困境。

首先是简短形式,它完全混淆了编译器实际上在后台创建了一个委托对象。创建对象并不是你想隐藏在地垫下的任何东西,就像你喜欢相信垃圾收集器总是能把它做好一样。不幸的是,它没有,这给使用需要函数回调的 pinvoke 的程序员带来了麻烦。经典案例是 SetWindowsHookEx(),它使用回调让 Windows 传递挂钩事件的通知。很常见的写法是这样的:

hhook = SetWindowsHookEx(WH_KEYBOARD_LL, hookProc, hInstance, 0);

其中hookProc是回调方法。那是行不通的,保证几秒钟后程序就会崩溃。C# 编译器自动创建委托对象以允许调用 hookProc 并将其传递给 SetWindowsHookEx 函数。麻烦的是,在任何地方都没有对该对象的实时引用。垃圾收集器看不到本机代码正在使用该对象。因此,下一次垃圾清扫会破坏该对象,当 Windows 稍后进行回调时,这就是一个 Big Kaboom。至少对于长格式,C# 程序员有可能意识到他最好将对象引用存储在其他地方。

长表格也很麻烦,不够长。它完全混淆了 C# 编译器实际上创建了一个使用this引用的委托对象。当委托目标是实例方法时会发生这种情况。当 C# 程序员编写如下代码时,这会出错:

        SystemEvents.UserPreferenceChanged += 
           new UserPreferenceChangedEventHandler(repaintControls);

其中 repaintControls 是表单的一个实例方法,当用户更改主题时会重新绘制所有控件。这里的问题是委托对象捕获了this的值并将其分配给静态事件。除非您明确编写代码以再次注销事件处理程序,否则它将永远保留表单对象的引用。这个要求并不明显,当然不是来自糖,否则是 Winforms 编程中非常不常见的要求。然而,这是一个非常讨厌的内存泄漏错误,当它运行足够长的时间时,它很容易使您的程序因内存不足异常而崩溃。

好吧,这两种语法都不是完美的,而且 C# 语言不允许像 C++/CLI 那样显式分配委托目标对象的完整语法。IDE 团队不得不在困难和困难之间做出选择。他们捡起了石头。VS11会有硬的地方,无疑是受到大众需求的启发。

于 2012-05-06T15:05:07.010 回答