Code Reading #1: Slick callback registration in MonoDevelop

I’m starting a new category of blog posts called ”Code Reading” where I’ll talk about something interesting I saw during my week of coding, with some code snippets of course.

Just today, this caught my eye when I was trying to figure out how to hook into an event in the Vim addin that I was trying to package up. So there is a global preferences window where the user checks boxes to enable certain things, and one of them is vim input mode in the text editor. Basically, there’s a global PropertyService that exposes these selections, as well as fires an event when something is updated.

I found an example of how this event can be registered/unregistered in the SourceEditorOptions class. Registration happens in the constructor:

        DefaultSourceEditorOptions (MonoDevelop.Ide.Gui.Content.TextStylePolicy currentPolicy)
        {   
            LoadAllPrefs (); 
            UpdateStylePolicy (currentPolicy);
            PropertyService.PropertyChanged += UpdatePreferences;
            ... 

And unregistration happens in the destructor:

        public override void Dispose()
        {   
            PropertyService.PropertyChanged -= UpdatePreferences;
            FontService.RemoveCallback (UpdateFont);
        } 

So PropertyChanged shows up in PropertyService on line 270:

public static event EventHandler<PropertyChangedEventArgs> PropertyChanged;

The interesting thing to me is the event keyword, which means there’s language-level support for event handling.

It’s really slick syntax to just add or subtract your event handler to the event as the highlighted lines above show. Let’s just peek at what that event handler looks like:

        void UpdatePreferences (object sender, PropertyChangedEventArgs args)
        {   
            try {
                switch (args.Key) {
                    case "TabIsReindent":
                    this.TabIsReindent = (bool)args.NewValue;
                    break;
                    case "EnableSemanticHighlighting":
                    this.EnableSemanticHighlighting = (bool)args.NewValue;
                    break;
                    ... 

So it’s interesting that you can just pass the member function around. There’s magic that happens inferring the type of that thing, but the point is that it is a first-class object. Here’s another more explicit example elsewhere:

            properties.PropertyChanged += delegate(object sender, PropertyChangedEventArgs args) {
                if (PropertyChanged != null)
                    PropertyChanged (sender, args);
            }; 

I guess it’s a lambda function, but not quite, because they call it a delegate. But it has the signature and the body, all the same.

Well, I was really impressed when I tried to plug my own function in and got a compiler error indicating what the signature should have been. I wonder where it is specified and also where the event is generated. It’s a mystery to me, but I know that it’s a lot more heavyweight to achieve in C++.

Advertisements