CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Page 1 of 2 12 LastLast
Results 1 to 15 of 22
  1. #1
    Join Date
    Jun 2010
    Location
    Germany
    Posts
    2,675

    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,543

    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,675

    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,543

    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,675

    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,133

    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,543

    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,675

    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,133

    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
    Jun 2010
    Location
    Germany
    Posts
    2,675

    Re: Event handlers vs. base class method overrides

    Quote Originally Posted by TheGreatCthulhu View Post
    Obviously, I was delayed...
    Never mind. I'm not really reacting in real-time at the time either. Need to catch up with some sleep deficit I accumulated but my attempts in that direction haven't been really effective yet.

    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...).
    I wouldn't feel comfortable to rely on that, though: Any probability of success below 1.0 is called a risk. (Or, more precisely, the difference between 1.0 and the actual probability is called the risk. ) But what you point out later on about the delegate holding a reference to the object that is to send the event shows that's in fact no problem at all. I think most developers (including myself) don't always positively keep that in mind, though it can be deduced from the parameters passed to the delegate constructor.

    BTW, in that respect the way I construct the mouse click event handler delegate in my code from the OP is apparently faulty:

    Code:
      pentbox->MouseClick += gcnew MouseEventHandler(this, &Form1::pentbox_MouseClick);
    Keep in mind that this is a line in a Form1 method, not a PentagramBox one. Shouldn't that rather be the following?

    Code:
      pentbox->MouseClick += gcnew MouseEventHandler(pentbox, &Form1::pentbox_MouseClick);
    Maybe this just isn't causing noticable problems because I don't use the sender parameter passed to the handler at all.

    OTOH, the button's click handler is set up like that in Form1::InitializeComponent():

    Code:
          this->bnCreateShape->Click += gcnew System::EventHandler(this, &Form1::bnCreateShape_Click);
    Now I'm a bit confused...

    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.
    On second thought, for a similar reason self-destroying objects are even safe (to some extent) in native C++: The member function diong the self-destruction just must not access any data members of its own object after calling delete, neither directly nor indirectly. Data members get destroyed, member functions don't. Of course this design is still to be considered risky because it's easy to break during code changes, and, as usual, that risk increases with the complexity of the scenario.

    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.
    Yes, it's not uncommon to see beginners here happily mixing managed and unmanaged stuff without any need and then being surprised by the problems they encounter. Another indication for that confusion is that they often have problems to pick the right forum section for their post (OTOH, some of these programs are so hybrid that this decision really isn't easy to make... ) This is not a safe indication however, because some people simply always pick the first section in any forum due to being even too lazy to read the section descriptions (or even titles)...

    BTW, the problems experienced by .NET programmers when entering the native C++ world apply to Java programmers as well in a quite similar manner...
    Last edited by Eri523; March 24th, 2011 at 11:30 PM.
    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.

  11. #11
    Join Date
    Jan 2010
    Posts
    1,133

    Re: Event handlers vs. base class method overrides

    Quote Originally Posted by Eri523 View Post
    Never mind. I'm not really reacting in real-time at the time either. Need to catch up with some sleep deficit I accumulated but my attempts in that direction haven't been really effective yet.
    Oh, I was more talking to myself. It's a Cthulhu thing...

    C:≈

    [Cthulhu smileyTM]

    Quote Originally Posted by Eri523 View Post
    I wouldn't feel comfortable to rely on that, though
    Yeah, unless the docs explicitly state whether this is completely safe or not, with regard to how exactly the GC behaves.
    So, with that in mind, you can either let the GC collect the object, or change the design.

    Quote Originally Posted by Eri523 View Post
    I think most developers (including myself) don't always positively keep that in mind, though it can be deduced from the parameters passed to the delegate constructor.

    BTW, in that respect the way I construct the mouse click event handler delegate in my code from the OP is apparently faulty:

    Code:
      pentbox->MouseClick += gcnew MouseEventHandler(this, &Form1::pentbox_MouseClick);
    Keep in mind that this is a line in a Form1 method, not a PentagramBox one. Shouldn't that rather be the following?

    Code:
      pentbox->MouseClick += gcnew MouseEventHandler(pentbox, &Form1::pentbox_MouseClick);
    Maybe this just isn't causing noticable problems because I don't use the sender parameter passed to the handler at all.

    OTOH, the button's click handler is set up like that in Form1::InitializeComponent():

    Code:
          this->bnCreateShape->Click += gcnew System::EventHandler(this, &Form1::bnCreateShape_Click);
    Now I'm a bit confused...
    Aah, but the first parameter here is not the sender object.
    You pass the sender and the event arguments to the method being invoked; this is however, the delegate's constructor that takes that callback as a parameter.

    Here, take a look at the constructor, as defined by the underlying system:
    Code:
    MulticastDelegate(
    	Object^ target, 
    	String^ method
    )
    Parameters

    target
    Type: System::Object
    The object on which method is defined.

    method
    Type: System::String
    The name of the method for which a delegate is created.
    [emphasis by me]
    So, the original code is correct.
    In C#, the delegate's constructor appears to take just one parameter - the method. You can even "skip" the constructor and write:
    Code:
    // C# code
    pentbox.MouseClick += pentbox_MouseClick;
    Neat, huh?
    But this also means that the created dependency it's even less obvious in C#, especially to beginners.

    Quote Originally Posted by Eri523 View Post
    BTW, the problems experienced by .NET programmers when entering the native C++ world apply to Java programmers as well in a quite similar manner...
    I'm under the impression that it's significantly easier to those who transition from C# to Java, as they are similar in many respects. (And the same possibly goes for some other .NET languages as well).
    Last edited by TheGreatCthulhu; March 25th, 2011 at 08:15 AM.

  12. #12
    Join Date
    Jun 2010
    Location
    Germany
    Posts
    2,675

    Re: Event handlers vs. base class method overrides

    Quote Originally Posted by TheGreatCthulhu View Post
    Aah, but the first parameter here is not the sender object.
    You pass the sender and the event arguments to the method being invoked; this is however, the delegate's constructor that takes that callback as a parameter.

    Here, take a look at the constructor, as defined by the underlying system:

    [...]

    So, the original code is correct.
    Fascinating! I was referring to the MouseEventHandler Delegate page on MSDN when writing this and was apparently mistaken:

    Quote Originally Posted by MSDN
    Code:
    public delegate void MouseEventHandler(
    	Object^ sender, 
    	MouseEventArgs^ e
    )
    Parameters

    sender
    Type: System::Object
    The source of the event.

    e
    Type: System.Windows.Forms::MouseEventArgs
    A MouseEventArgs that contains the event data.
    But that seems to be wron anyway as I just noticed: The second paramneter is in no way a MouseEventArgs ^ (or I might have been on the wrong page).

    This doesn't seem to be a real constructor in the usual way at all: The parent of that documentation page is the System::Windows::Forms namespace page, not that of a cass.

    In C#, the delegate's constructor appears to take just one parameter - the method. You can even "skip" the constructor and write:
    Code:
    // C# code
    pentbox.MouseClick += pentbox_MouseClick;
    Neat, huh?
    But this also means that the created dependency it's even less obvious in C#, especially to beginners.
    Yes, neat and convenient indeed. But just what I say: C# does lots of things implicitly under the hood which is admittedly convenient and maybe even increases code readability, but it also often obscures what's actually going on.

    I'm under the impression that it's significantly easier to those who transition from C# to Java, as they are similar in many respects. (And the same possibly goes for some other .NET languages as well).
    Well, I actually meant the transition from Java to native C++. Every once a while you can find obvious examples of that in the Non-VC+ section.
    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.

  13. #13
    Join Date
    Jan 2010
    Posts
    1,133

    Re: Event handlers vs. base class method overrides

    Quote Originally Posted by Eri523 View Post
    ( [...] I might have been on the wrong page)
    You could say that.

    Quote Originally Posted by Eri523 View Post
    Code:
    public delegate void MouseEventHandler(
    	Object^ sender, 
    	MouseEventArgs^ e
    );
    [...]
    This doesn't seem to be a real constructor in the usual way at all: The parent of that documentation page is the System::Windows::Forms namespace page, not that of a cass.
    That's because it's not. It's a declaration of a delegate type MouseEventHandler - kind of similar to when you declare a function pointer in C++. (It's declared in the System::Windows::Forms namespace.) However, delegates in .NET are full-blown types. As you know, all the usual .NET types implicitly derive from the Object class - and not always directly. There are types that support the language infrastructure, types that can't be used (instantiated/derived from) the normal way. MulticastDelegate is one of them; all the declared delegates implicitly derive from it (I think - some may derive from Delegate; but these are internal implementation details which are not important to us). As any other class, it provides a public interface, but it's generally of little direct use to developers.
    (Just in case you missed it: I also provided a link to the MSDN page, but I've put it inside the [code][/code] section.)

    Quote Originally Posted by Eri523 View Post
    Yes, neat and convenient indeed. But just what I say: C# does lots of things implicitly under the hood which is admittedly convenient and maybe even increases code readability, but it also often obscures what's actually going on.
    On the other hand, that's not such a bad thing. This is one of the basic principles of OOP - the client doesn't need to (and shouldn't) know the internal details of the class. Similarly, it would be bad if, to code properly, you needed to know all the implementation details and intricacies of the language. C++ goes a bit against that, but as I've said - different design goals.
    For example, the fact that assigning an event handler also creates a reference might be a bit obscured, but the docs and any good introductory C# (or .NET) book or course should mention it. Besides, when you think about it a bit, you realize: well, yeah - what else. Not knowing this detail isn't fatal, but knowing about it can help you create a better design and write better .NET code.

  14. #14
    Join Date
    Jun 2010
    Location
    Germany
    Posts
    2,675

    Re: Event handlers vs. base class method overrides

    Quote Originally Posted by TheGreatCthulhu View Post

    ( [...] I might have been on the wrong page)
    You could say that.

    That's because it's not. It's a declaration of a delegate type MouseEventHandler - kind of similar to when you declare a function pointer in C++. (It's declared in the System::Windows::Forms namespace.)
    Sounds reasonable. But then what actually is the type of the expression marked in red in the snippet below?

    Code:
    this->bnCreateShape->Click += gcnew System::EventHandler(this, &Form1::bnCreateShape_Click);
    And where on MSDN is the documentation on that and this System::EventHandler "constructor"?

    However, delegates in .NET are full-blown types. As you know, all the usual .NET types implicitly derive from the Object class - and not always directly. There are types that support the language infrastructure, types that can't be used (instantiated/derived from) the normal way. MulticastDelegate is one of them; all the declared delegates implicitly derive from it (I think - some may derive from Delegate; but these are internal implementation details which are not important to us). As any other class, it provides a public interface, but it's generally of little direct use to developers.
    Yes, I've already come across these "helper types". I think System::Enum is another one of them, as well as System::ValueType. And even if it was possible to instantiate them directly, why should anyone want to do that?

    (Just in case you missed it: I also provided a link to the MSDN page, but I've put it inside the [code][/code] section.)
    Don't worry, I haven't missed that link. Actually, the page it points to was the first thing I read after your post containing the link. But it looks as if that MSDN page rose more questions than it answered. A major part of what I ask in this post and my last one in this thread has been caused by that page, directly or indirectly.

    On the other hand, that's not such a bad thing. This is one of the basic principles of OOP - the client doesn't need to (and shouldn't) know the internal details of the class. Similarly, it would be bad if, to code properly, you needed to know all the implementation details and intricacies of the language.
    Yes, that's encapsulation, a generally accepted and appreciated concept nowadays, and a fundamental part of the concept of many programming languages and frameworks used at the time.

    C++ goes a bit against that, but as I've said - different design goals.
    You're talking of C++/CLI here, aren't you? Native C++ is a generally accepted language while C++/CLI still is subject to controversial discussions.

    For example, the fact that assigning an event handler also creates a reference might be a bit obscured, but the docs and any good introductory C# (or .NET) book or course should mention it. Besides, when you think about it a bit, you realize: well, yeah - what else. Not knowing this detail isn't fatal, but knowing about it can help you create a better design and write better .NET code.
    Well, experienced programmers using .NET languages just as an extension of their repertoire are certainly interested in sub-surface details like that, but one of the main reasons why they are is because they know out of experience that something like that must exist somewhere. Beginners, OTOH, are IMO most likely happy with the convenience of .NET and don't read the books you're talking about, not only because they don't know the reasons why they should do that, but also because they don't know the reasons why those reasons exist...

    Watch out: You're talking to an assembly language programmer! I still like to see the details, not just to simply know they're there, but also to help me to understand how the whole thing works.
    Last edited by Eri523; March 25th, 2011 at 08:48 PM.
    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.

  15. #15
    Join Date
    Jan 2010
    Posts
    1,133

    Re: Event handlers vs. base class method overrides

    Quote Originally Posted by Eri523 View Post
    Sounds reasonable. But then what actually is the type of the expression marked in red in the snippet below?

    Code:
    this->bnCreateShape->Click += gcnew System::EventHandler(this, &Form1::bnCreateShape_Click);
    Good question - I've been wondering myself about that, too - as this constructor is pretty different to the one I'm used to in C# (were, aside from the shorthand syntax, you can also do it the regular way).

    Now we're talking internal details of the C++/CLI language. My C++ is definitely rusty, and although I had some idea, I had to do some digging to figure it out; besides, from what I've read, this is a not-so-well-known feature anyway (well, relatively).

    And I really wanted to understand all this; so, I downloaded C++/CLI specification from here (although I suspect that I already have in some obscure folder, as it might have installed with VS).
    I skimmed through the chapter that overviews delegates, and I found this:

    [Chapter 8.7, pg. 20]
    The constructor for a delegate needs two arguments when it is bound to a non-static member function: the first is a handle to an instance of a ref class, and the second designates the non-static member function within that ref class’s type, using the syntax of a pointer to member. The constructor for a delegate needs only one argument when it is bound to a static function, or a global- or namespace-scope function; the argument designates that function, using the syntax of a pointer to member or pointer to function, as appropriate.
    Which means that what you highlighted uses the pointer-to-member-function syntax. (For non-member or static function, the '&' is optional, for member functions it is required.) In native C++, a regular function pointer and a pointer-to-member have different internal representation (as pointed out here) - I'm guessing that it's similar in C++/CLI, and that this internal data structure is somehow mapped to what the CLR expects.

    BTW, this was really exciting, because while digging I've discovered something that I wasn't aware of; the thing is, native C++ pointers to member functions support polymorphism, so if the inheriting class overrides the base member, the derived implementation gets called. It made me wonder if the same goes for delegates in .NET, and my first quick tests indicate that it does! I had no idea!

    And where on MSDN is the documentation on that and this System::EventHandler "constructor"?
    No clue; up until now I've only found it used in the examples, but I'm yet to see the page dedicated to it.

    Yes, I've already come across these "helper types". I think System::Enum is another one of them, as well as System::ValueType. And even if it was possible to instantiate them directly, why should anyone want to do that?
    I'm not saying that one would want to - I'm just remarking that it can't be done by design. Anyway, I wouldn't call them "helper types", as these directly support .NET infrastructure - these are at the very core of the .NET type system.

    You're talking of C++/CLI here, aren't you? Native C++ is a generally accepted language while C++/CLI still is subject to controversial discussions.
    I was talking both native C++ and C++/CLI, but honestly, I had native C++ in mind. Don't get me wrong: I'm not questioning C++ as a language - I'm just saying that C++ is not "hardcore" OO - as opposed to languages on the other extreme, like Smalltalk (where even classes are objects, whose class is Metaclass... o_O). For example, C# is a bit "more OO" in the sense of design, one of the reasons being the fact that it's impossible to declare a method outside of a class. C++ doesn't impose such restrictions - and it shouldn't as it would go against the core features of the language. OOP principles are really good design principles, but as you yourself noted, practical considerations (including the current evolutionary state of software and hardware) sometimes dictate that things need to be done differently in certain aspects.

    Watch out: You're talking to an assembly language programmer!


    I still like to see the details, not just to simply know they're there, but also to help me to understand how the whole thing works.
    Oh, I like to get to know the details myself - as I've said, understanding how it works helps writing better code; what I'm saying is that sometimes the details leak out to the interface exposed to the client (I'm using the words 'interface' and 'client' in a very broad sense here) where they don't belong, thus impacting the usability of the interface.
    If language syntax is the this interface used by developers (clients) to communicate to the compiler, then I might have some dissatisfaction with it.

    For example, native C++ provides delete, and delete[] - this has probably been pain for anyone learning C++. But, really, why is such a distinction needed? From the perspective of the developer (client) - who cares?! Why not just one keyword - internally, the compiler could transform it to whatever it wants.
    Then, take the function pointers and pointers to member functions. There is a difference, sure, internal representations are different, but again - why do I need to know this?
    Or take the C++ style casting operators. To quote a page I linked to in my final post in this thread:
    There’s another reason why C++ pundits scorn C-style cast -- the same cast expression may perform two conversions at once:
    Code:
    const unsigned int *lp; //ptr to const unsigned int
    (int *) lp; //get rid of unsigned and const at once.
    And I say: so what? If the programmer made a mistake here, that’s too bad. Then again, programmers make much more mistakes by confusing delete and delete[]. Besides, what if the programmer had truly meant to use two cast operations in one expression? Should the programming language twist his arm and force him to use two different cast expressions?
    Hm..., he makes a similar remark on delete/delete[].

    Anyway, I'm not prepared to unconditionally defend these views - I'm just discussing them. Of course, we don't have to agree.
    Last edited by TheGreatCthulhu; March 26th, 2011 at 08:02 PM.

Page 1 of 2 12 LastLast

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
  •  





Click Here to Expand Forum to Full Width

Featured