简体   繁体   中英

Why does Visual Studio autogenerate long EventHandlers?

Consider the following code:

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

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

In VisualStudio, if you type PropertyChanged += , the application prompts you to press tab to generate an event handler that looks like the first line of my constructor. However, it is also valid (and more concise) to write this like the second line in my constructor.

Do these two lines have different meaning? If not, why does VisualStudio like to generate the former?

Statement syntax is pretty subjective and you can't argue about taste. But notable is that both forms of the statement are syntax sugar in C#. The chronic problem with sugar is that it produces rotten teeth. This kind of syntax hides what is really going on and that forever get C# programmers in trouble.

The short form first, it completely obfuscates that the compiler actually creates a delegate object under the hood. Getting objects created is not really ever anything you want to hide under a floor mat, as much as you like to trust the garbage collector to always get it right. Unfortunately it doesn't, this gets programmers in trouble that use pinvoke that requires a function callback. The classic case is SetWindowsHookEx(), it uses a callback to let Windows deliver the notifications for hooked events. Which is very commonly written like this:

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

Where hookProc is the callback method. That doesn't work, it is guaranteed to crash the program after a few seconds. The C# compiler automatically creates the delegate object to allow hookProc to be called and passes it to the SetWindowsHookEx function. Trouble is, there is no live reference to that object anywhere. The garbage collector cannot see that the native code is using that object. So the next garbage sweep destroys the object and that's a Big Kaboom when Windows makes the callback later. At least with the long form, there are some odds that the C# programmer realizes he'd better store the object reference somewhere else.

The long form is trouble too, it isn't long enough. It completely obfuscates that the C# compiler actually creates a delegate object that uses the this reference. Which happens when the delegate target is an instance method. This goes wrong when a C# programmer writes code like this:

        SystemEvents.UserPreferenceChanged += 
           new UserPreferenceChangedEventHandler(repaintControls);

Where repaintControls is an instance method of a form that repaints all the controls when the user changed the theme. What goes wrong here is that the delegate object captures the value of this and assigns it to a static event. Which forever leaves the form object referenced, unless you explicitly write code to unregister the event handler again. This requirement is just not obvious, certainly not from the sugar and otherwise a very uncommon requirement in Winforms programming. It is however a very nasty memory leak bug that can easily make your program fall over on an out-of-memory exception when it runs long enough.

Well, neither syntax is perfect and the C# language doesn't permit the full syntax like C++/CLI does that explicitly assigns the delegate target's object. The IDE team had to pick between a rock and a hard place. They picked the rock. VS11 will have the hard place, no doubt inspired by popular demand.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM