Event handlers vs. base class method overrides
CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 15 of 22

Thread: Event handlers vs. base class method overrides

Hybrid View

  1. #1
    Join Date
    Jun 2010
    Location
    Germany
    Posts
    2,591

    Event handlers vs. base class method overrides

    This is a spin-off of another thread. I detached it because it was a bit off-topic over there and could be the beginning of another interesting discussion. Also, the original thread has already been marked [RESOLVED] so some people might miss it there.

    Quote Originally Posted by TheGreatCthulhu View Post
    [...] .NET events are essentially just a syntactic sugar for what is in its core the Observer pattern. Events are a form of the public interface, so in that sense, handling an event is no different from calling public methods or using public properties. It can break encapsulation in the same way a method can (by directly exposing some internal aspect of the class). Semantically, events are meant to provide a notification when some state changes, so that event clients (observers) might respond to them.

    For example, a button usually does not handle its own Click event, but some other, external class. The class that fires the events "knows" when the internal state changes, since (if the encapsulation principle was respected) it is the one that changes the state. In that sense, there's generally no need for a class to handle its own events, unless they come from a parent class, and no other mechanism exist.
    Just as sometimes a method can break encapsulation by exposing a pointer or a .NET handle of an internal object, so that an external class might alter it's state, so can an event.
    Sometimes, this behavior is desirable, sometimes it's not.

    In part, it's a matter of preference, and in part is driven by the requirements.
    Oops... I actually failed to observe (pun intended ) the fact that there's a solid, generally accepted design pattern that backs MS's concept of event handlers in .NET.

    My preference for overloads of the base class' On...() methods (if I already have a derived class anyway) may be a heritage from the MFC code I've written: It's pretty much the standard procedure there. Also, MSDN frequently states that overriding these methods is the preferred way to handle events in the "Notes to inheritors" section of the method documentation. I haven't found an example of the phrase on MSDN in a quick search, but IIRC it says (or at least means) that it's the preferred method if you already have a derived class. I think registering an event handler as a delegate with the base class from the derived class looks weird and not reasonable to me anyway.

    The example of the button click handler you give seems to be well chosen. Unless the action to be triggered by the button is close to trivial, even in MFC the click event handler would most likely call out to some other code. And I think that, though the call to them is implemented in a much fancier way, the .NET event handlers are essentially nothing else than that code that gets called.

    As already mentioned in the original thread, I have recently written a demo project for another thread which contains a class that uses (resp. is used with) a combination of both base class method overrides and event handlers. I didn't actually post the code in the other thread because it was for a homework assignment, but that was almost a month ago and so I think the homework has already been handed in in the meantime anyway. So I think I can post the code here and now as a basis for discussion.

    The thread the code originally was written for contains additional discussion about the code and in particular the description of the objective of the assignment in post #1.

    The code generates a draggable shape on the form in response to a button click. The shape gets destroyed when it itself is right-clicked. The shape in question is a pentagram and what's going on inside the class that draws it isn't really spectacular and doesn't matter here. So I'll just post the .h file declaring that class' interface:

    Code:
    // Pentagram.h
    
    #pragma once
    
    using namespace System;
    using namespace System::Drawing;
    
    namespace Test7
    {
    
    public ref class Pentagram
    {
    public:
      Pentagram(Point location, float radius) : m_location(location), m_radius(radius)
      {}
    
      void SetLocation(Point location);
      void Draw(Graphics ^gr);
    
    private:
      Point m_location;
      float m_radius;
    
    private:
      Pentagram() {}  // Disable default construction
    };
    
    }
    The class that implements the actual draggable shape on the form (by using the Pentagram class above) is derived from PictureBox and is called PentagramBox:

    Code:
    // PentagramBox.h
    
    #pragma once
    
    #include "Pentagram.h"
    
    namespace Test7
    {
    
    using namespace System;
    using namespace System::Windows;
    using namespace System::Windows::Forms;
    using namespace System::Drawing;
    
    public ref class PentagramBox : public PictureBox
    {
    public:
      PentagramBox(Control ^parent, Rectangle bounds);
    
    protected:
      virtual void OnPaint(PaintEventArgs ^pe) override;
      virtual void OnMouseDown(MouseEventArgs ^e) override;
      virtual void OnMouseUp(MouseEventArgs ^e) override;
      virtual void OnMouseMove(MouseEventArgs ^e) override;
    
    private:
      Pentagram ^pent;
      bool dragging;
      Point lastmousepos;
    
    private:
      PentagramBox() {}  // Disable default construction
    };
    
    }
    Code:
    // PentagramBox.cpp
    
    #include "StdAfx.h"
    
    #include "PentagramBox.h"
    
    using namespace Test7;
    
    PentagramBox::PentagramBox(Control ^parent, Rectangle bounds)
    {
      Parent = parent;
      Bounds = bounds;
      pent = gcnew Pentagram(Point(bounds.Width / 2, bounds.Height / 2), Math::Min(bounds.Width / 2, bounds.Height / 2));
      dragging = false;
    }
    
    void PentagramBox::OnPaint(PaintEventArgs ^pe)
    {
      __super::OnPaint(pe);
      pent->Draw(pe->Graphics);
    }
    
    void PentagramBox::OnMouseDown(MouseEventArgs ^e)
    {
      if (e->Button == Forms::MouseButtons::Left) {
        dragging = true;
        lastmousepos = e->Location;
      }
      __super::OnMouseDown(e);
    }
    
    void PentagramBox::OnMouseUp(MouseEventArgs ^e)
    {
      if (e->Button == Forms::MouseButtons::Left) dragging = false;
      __super::OnMouseUp(e);
    }
    
    void PentagramBox::OnMouseMove(MouseEventArgs ^e)
    {
      if (dragging) {
        Drawing::Size movevect(e->Location.X - lastmousepos.X, e->Location.Y - lastmousepos.Y);
        Location += movevect;
      }
      __super::OnMouseMove(e);
    }
    You see, almost all aspects of mouse handling are done inside the class itself which provides better encapsulation. Only the right-click that destroys the draggable shape is handled in an event handler inside the form class. These are the form class' event handlers:

    Code:
    // Form1.cpp
    
    #include "stdafx.h"
    
    #include "Form1.h"
    
    using namespace Test7;
    
    System::Void Form1::bnCreateShape_Click(System::Object^  sender, System::EventArgs^  e)
    {
      pentbox = gcnew PentagramBox(this, Rectangle(10, 10, 42, 42));
      pentbox->MouseClick += gcnew MouseEventHandler(this, &Form1::pentbox_MouseClick);
      bnCreateShape->Enabled = false;
    }
    
    void Form1::pentbox_MouseClick(Object ^sender, MouseEventArgs ^e)
    {
      if (e->Button == Forms::MouseButtons::Right) {
        delete pentbox;
        pentbox = nullptr;
        bnCreateShape->Enabled = true;
      }
    }
    I handled that single mouse action differently because I don't feel comfortable with self-destroying objects. And even what I actually did here looks suspicious: Doesn't the event handler return to something that has already been destroyed? Probably .NET's lazy destruction and the fact that tracking handles are reference counted saved my day here. Think I better shouldn't try that in native C++...

    Another reason for handling it that way, aside from being reluctant about self-destroying objets, is that the event handler also manipulates form class members that are not visible to the PentagramBox class.

    Any thoughts?

    I also have attached the complete project (VS 2010) to this post. Unlike the ZIP file, the project inside is named Test7, but I already have uploaded a Test7.zip in another thread, and though uploading files with the same name to different threads would even work, I wanted to make sure to avoid confusion. Of course that project also contains the code files I didn't post here.
    Attached Files Attached Files
    I was thrown out of college for cheating on the metaphysics exam; I looked into the soul of the boy sitting next to me.

    This is a snakeskin jacket! And for me it's a symbol of my individuality, and my belief... in personal freedom.

  2. #2
    Join Date
    Jul 2002
    Posts
    2,517

    Re: Event handlers vs. base class method overrides

    I am not sure what you actually ask. But your code looks a bit strange:
    delete pentbox;
    C++/CLI delete operator for ref classes is mapped to Dispose method. Since PentagramBox is not disposable, this line doesn't do anything. But this shows that you don't understand completely ref class lifetime cycle and .NET references.

    Regarding event handlers vs. overrides. Event handler doesn't require for client code to create derived class. Client only needs to subscribe to event. Also, events allow multiple clients to subscribe to the same event, which is impossible in inheritance model.

    On the other side, inheritance allows to build projects hierarchy, when every derived class adds its own functionality (you can read such junk in every OOP book, I am not so strong in a theory...).

  3. #3
    Join Date
    Jun 2010
    Location
    Germany
    Posts
    2,591

    Re: Event handlers vs. base class method overrides

    Quote Originally Posted by Alex F View Post
    C++/CLI delete operator for ref classes is mapped to Dispose method. Since PentagramBox is not disposable, this line doesn't do anything. [...]
    By "is not disposable" you mean "doesn't implement IDiaposable" (neither in the form of a "destructor" nor otherwise), don't you? Of course you're right. The member variables of PentagramBox are only value types except for the Pentagram, which in turn holds only value type members. So why should it implement IDisposable? I don't see any need for specific disposal handling here.

    Control does implement IDisposable, though, and it is the direct ancestor of PictureBox from which PentagramBox is derived from. Doesn't the IDisposable implementation get inherited? (I assume PictureBox adds its own Dispose() handling to the inheritance chain but I'm not 100% sure.)

    From a naive point of view: The delete statement at least superficially does what I want it to do: It instantly removes the shape from the form, so what? I don't have specific disposal requirements here and the resources held by the base classes are their own responsibility.

    [...] But this shows that you don't understand completely ref class lifetime cycle and .NET references.
    You're probably right: I entered the .NET world just about half a year ago and am still learning. But IMHO .NET itself isn't completely innocent in that respect: It does quite complex things under the hood that, although the developer may actually be able to influence them, he usually doesn't need to worry about. This is quite convenient but encourages a lazy programming style one could never get away with in native C++, for instance.

    Regarding event handlers vs. overrides. Event handler doesn't require for client code to create derived class. Client only needs to subscribe to event. Also, events allow multiple clients to subscribe to the same event, which is impossible in inheritance model.
    Yes, the .NET event handler concept is really convenient (yet again ). However, the override concept in some way allows multiple subscribers too: The event can get passed up the inheritance hierarchy by calling the base class' On...() implementation. Multiple subscribers on the same inheritance level can simply be called from the On...() implementation. And, if I understand it correctly, even the handlers registered according to the .NET event model are essentially called from the On...() implementation of the class that holds them.

    In the inheritance model, an On...() implementation can simply break the chain of passing the event up the hierarchy by not calling its base class pendant. I believe in the .NET event model something similar is possible, in that one handler is able to remove another one from the event handlers collection. But that looks more complicated to me and I doubt it would (always) be good style...
    I was thrown out of college for cheating on the metaphysics exam; I looked into the soul of the boy sitting next to me.

    This is a snakeskin jacket! And for me it's a symbol of my individuality, and my belief... in personal freedom.

  4. #4
    Join Date
    Jul 2002
    Posts
    2,517

    Re: Event handlers vs. base class method overrides

    Sorry, didn't understand your code, pentbox is PictureBox-derived, and applying delete (Dispose) to it before setting the reference to nullptr is a good programming style, which is observed among C++ (and not VB and C#) developers. Assuming, of course, that this is the last reference
    Looking at these code fragments, I don't see how this PictureBox is displayed on the form. Generally, this should be done by adding it to the Form.Controls collection. To remove it from the form, it should be removed from this collection.
    Setting last reference to nullptr actually kills .NET instance, making it candidate for garbage collection. Disposing before last reference is set to null releases unmanaged resources immediately (without waiting for GC).

  5. #5
    Join Date
    Jun 2010
    Location
    Germany
    Posts
    2,591

    Re: Event handlers vs. base class method overrides

    Quote Originally Posted by Alex F View Post
    Looking at these code fragments, I don't see how this PictureBox is displayed on the form. Generally, this should be done by adding it to the Form.Controls collection. To remove it from the form, it should be removed from this collection.
    The PentagramBox contructor has a parameter of type Control ^ to which the form class passes its this pointer. This is then assigned to the (inherited) PentagramBox::Parent property. MSDN says the following about that property:

    Quote Originally Posted by MSDN
    Setting the Parent property value to nullptr removes the control from the Control::ControlCollection of its current parent control.
    Although not mentioned (at least not on that page), assigning something else than nullptr to Parent does the opposite: It causes the control to add itself to the parent's Controls collection. This is quite unproblematic because it's just been given a handle to that parent. One of the Dispose() implementaions (probably the one in Control) obviously later takes care of the control removing itself from the parent's Controls collection.

    Actually, I don't remember where I read that, whether I simply did it without thinking about it because this behaviour is quite natural (and was just lucky) or tried it conciously because it looks natural.

    Setting last reference to nullptr actually kills .NET instance, making it candidate for garbage collection. Disposing before last reference is set to null releases unmanaged resources immediately (without waiting for GC).
    And the latter is certainly fatal (at least if some other code actually tries to use the object instead of just destroying the tracking handles to it). I never encountered problems with that because I handle that carefully obeying the object owner concept, most likely a habit I took over from native C++ where making the same mistake, just with a pointer instead of a tracking handle, is fatal as well. (Also, explicit deletion resp. disposal actually is rarely needed in .NET, further reducing the risk of accidents like that.)
    I was thrown out of college for cheating on the metaphysics exam; I looked into the soul of the boy sitting next to me.

    This is a snakeskin jacket! And for me it's a symbol of my individuality, and my belief... in personal freedom.

  6. #6
    Join Date
    Jan 2010
    Posts
    1,099

    Re: Event handlers vs. base class method overrides

    Quote Originally Posted by Alex F View Post
    I am not sure what you actually ask.
    Oh, If I may clarify, since this is an thread emerged from another thread:
    Eri523 just wanted to start a discussion about pros and cons of overriding On{Event} methods vs using event handlers, in derived classes.

    Quote Originally Posted by Alex F View Post
    Sorry, didn't understand your code, pentbox is PictureBox-derived, and applying delete (Dispose) to it before setting the reference to nullptr is a good programming style, which is observed among C++ (and not VB and C#) developers. Assuming, of course, that this is the last reference
    What you mean "and not VB and C# developers"? In C#, after you use a class that uses unmanaged resources, and you're sure it's not going to be needed anymore, you call the Dispose() method, and set the variable to null.
    Now, I don't know about the VB.NET developers, but I guess that the same guidelines apply to them. Besides, these programming practices aren't language-specific all that much.

    Quote Originally Posted by Eri523 View Post
    Setting last reference to nullptr actually kills .NET instance, making it candidate for garbage collection. Disposing before last reference is set to null releases unmanaged resources immediately (without waiting for GC).
    And the latter is certainly fatal (at least if some other code actually tries to use the object instead of just destroying the tracking handles to it). I never encountered problems with that because I handle that carefully obeying the object owner concept, most likely a habit I took over from native C++ where making the same mistake, just with a pointer instead of a tracking handle, is fatal as well. (Also, explicit deletion resp. disposal actually is rarely needed in .NET, further reducing the risk of accidents like that.)
    Well, I wouldn't say "kills", but otherwise, Alex F is right: once the last reference is released, the object becomes a candidate for the garbage collection. Most C++ developers seem to be a bit uncomfortable with this aspect of .NET: the control over the object's lifetime is taken out of their hands. But, the GC is very good at what it does. It's very efficient. Besides, this concept is at the very core of .NET: the underlying system does the cleanup. This eliminates a whole category of bugs related to native pointers and memory/resource management.

    So, you would dispose of the unmanaged resources only if you're sure some other object won't use the disposed object afterward.
    If not, and you don't need the object anymore, you just set it to nullptr so that the reference count drops.

    If Dispose() is never called by user code, it will eventually be called by the GC.

    P.S. I'll be right back, I have a comment on events.

  7. #7
    Join Date
    Jul 2002
    Posts
    2,517

    Re: Event handlers vs. base class method overrides

    Quote Originally Posted by Eri523 View Post
    And the latter is certainly fatal
    This is fatal if object is used after it is disposed. If you dispose the object and set its reference to nullptr, and this is the last object reference, this is absolutely OK.

  8. #8
    Join Date
    Jun 2010
    Location
    Germany
    Posts
    2,591

    Re: Event handlers vs. base class method overrides

    Quote Originally Posted by TheGreatCthulhu View Post
    [...] Besides, this concept is at the very core of .NET: the underlying system does the cleanup. This eliminates a whole category of bugs related to native pointers and memory/resource management.
    Yeah, I've already started to really appreciate this aspect of .NET. It doesn't only reduce the risk of certain bugs, it also is really convenient. But exactly that can be seen as a disadvantage as well: Not needing to care about object destruction (in most cases) encourages a somewhat lazy programming style that would be really bad in the major part of the world outside of .NET. I'm particularly concerned about people who learn programming on .NET and then suddenly get pushed into the cold water of native C++...

    If Dispose() is never called by user code, it will eventually be called by the GC.
    Yes, and for exactly that reason I find it really handy and important to be able to call Dispose() explicitly. For instance, I have a class holding the application's config. It writes any changes to the config back out to disk in its "destructor". To enforce this, I call delete on the config object in the main form class' destructor.

    Of course there would be other ways of implementing that but I find it really stringent that way.

    That could'nt have been done at all if it wasn't possible to deterministically call Dispose(). I really wouldn't like to have not the slightest clue about when my config changes actually get written back.

    Quote Originally Posted by Alex F View Post
    This is fatal if object is used after it is disposed. If you dispose the object and set its reference to nullptr, and this is the last object reference, this is absolutely OK.
    Sorry, I if I wasn't clear enough about that: This is exactly what I meant and it shoud be expressed by the bracketed part of the sentence:

    Quote Originally Posted by Eri523 View Post
    And the latter is certainly fatal (at least if some other code actually tries to use the object instead of just destroying the tracking handles to it).
    Probably the smiley right in the middle of that was misplaced and messed it up.
    I was thrown out of college for cheating on the metaphysics exam; I looked into the soul of the boy sitting next to me.

    This is a snakeskin jacket! And for me it's a symbol of my individuality, and my belief... in personal freedom.

  9. #9
    Join Date
    Jan 2010
    Posts
    1,099

    Re: Event handlers vs. base class method overrides

    Quote Originally Posted by TheGreatCthulhu View Post
    P.S. I'll be right back, I have a comment on events.
    Obviously, I was delayed...

    Quote Originally Posted by Eri523 View Post
    I handled that single mouse action differently because I don't feel comfortable with self-destroying objects. And even what I actually did here looks suspicious: Doesn't the event handler return to something that has already been destroyed? Probably .NET's lazy destruction and the fact that tracking handles are reference counted saved my day here. Think I better shouldn't try that in native C++...
    Yeah. The OnMouseClick() protected function of PictureBox will raise the event, which essentially means invoking all the methods "stored" in the delegate (events are based on delegates). If one of those delegates happens to call Dispose() on the object, unmanaged resources will be released, but the object will continue to exist, as this is no different from any other function call. If the handler also happens to set the last reference (well, gc handle - in C# it's called a reference) to this object to nullptr, no big deal; this doesn't magically destroy the object at that very instance - just makes it eligible for garbage collection. When this happens exactly is for the GC to decide (it has it's own agenda: categorizes objects in 3 generations, according to some algorithm, etc...).
    So, after the handler function returns (or after all of them return), control is returned to OnMouseClick(), when it may preform some additional logic, before it returns itself. Now, remember - unmanaged resources are disposed, and the object is generally not usable, but it still exists.
    The function that receives control may perform additional tasks, and return, and so on, until the execution stops at some point for one reason or another.

    Now, how the runtime determines if it's safe to perform garbage collection is irrelevant (maybe it holds an internal reference to the object while a function runs or something) - what matters is that it can figure out when it should do it.

    BTW, note that when an event handler is assigned to an event, an implicit reference is created to the object that owns the handler function. Many people are not aware of this. In fact, if the only remaining reference is the one held by the event, the GC will not collect the object until the handler is removed.

    Quote Originally Posted by Eri523 View Post
    I'm particularly concerned about people who learn programming on .NET and then suddenly get pushed into the cold water of native C++...
    Those new to a .NET language (especially to C#), or those new to native C++, will often use something like "C/C++/C#" in their sentences. Of course, on both forums, such a statement is followed by a bunch of remarks about C/C++ and C# being only similar with regard to syntax, and that these are completely different languages. As such they are based on different concepts, and were made with different design goals in mind. It is important to make this distinction. C++/CLI, on the other hand, is a hybrid language, making things a bit complicated. I can imagine how a beginner might feel when confronted not only with everything C++ has to offer (which can be confusing in its own right), but also with all that blended with managed features, and all the .NET framework has to offer, altogether with rules about how to and how not to use managed and unmanaged code together.

    P.S. But most of the programming best practices are in their core not language-specific, especially if these are related to design considerations. The only thing that is tricky is knowing how to apply these practices to a specific language.
    Last edited by TheGreatCthulhu; March 24th, 2011 at 03:11 PM.

  10. #10
    Join Date
    Jan 2010
    Posts
    1,099

    Re: Event handlers vs. base class method overrides

    At that thread, the OP mentioned "The C++ Programming Language" by Bjarne Stroustrup, p. 128 - which contains a sentence that talks about the way the operators we were talking about are usually implemented:

    To deallocate space allocated by new, delete and delete [] must be able to determine the size of the object allocated. This implies that an object allocated using the standard implementation of new will occupy slightly more space than a static object. Typically, one word is used to hold the object's size.

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  


Windows Mobile Development Center


Click Here to Expand Forum to Full Width

This is a CodeGuru survey question.


Featured


HTML5 Development Center