views:

2171

answers:

4

I have a base class DockedToolWindow : Form, and many classes that derive from DockedToolWindow. I have a container class that holds and assigns events to DockedToolWindow objects, however I want to invoke the events from the child class.

I actually have a question about how to implement what this MSDN site is telling me to do. This section below is giving me the problem:

    // The event. Note that by using the generic EventHandler<T> event type
    // we do not need to declare a separate delegate type.
    public event EventHandler<ShapeEventArgs> ShapeChanged;

    public abstract void Draw();

    //The event-invoking method that derived classes can override.
    protected virtual void OnShapeChanged(ShapeEventArgs e)
    {
        // Make a temporary copy of the event to avoid possibility of
        // a race condition if the last subscriber unsubscribes
        // immediately after the null check and before the event is raised.
        EventHandler<ShapeEventArgs> handler = ShapeChanged;
        if (handler != null)
        {
            handler(this, e);
        }
    }

Sure this example compiles and works, but when I replace "ShapeChanged" with "Move" (an event I acquired from deriving from Form), it errors saying I cannot have Move on the right side without += or -=. I also removed the ShapeEventArgs generic tags.

Any incite on why this isn't working? What's the difference between an event declared within the class and one that is inherited?

+1  A: 

The difference is scope. Inside your class, you can control how your event delegates are handled, however, your class cannot control what the base class is doing. It might be doing some crazy behind-the-scenes stuff with the event and its handlers. If you simply "reassigned" the Move event, you would be wiping out the multicast delegate list for the event.

I'm guessing they put a compiler restriction on this because its a very unsafe practice, and would essentially give any descendant class the ability to destroy the event model of its parent.

womp
+1  A: 

From the C# language spec, section 10.7 (emphasis added):

Within the program text of the class or struct that contains the declaration of an event, certain events can be used like fields. To be used in this way, an event must not be abstract or extern, and must not explicitly include event-accessor-declarations. Such an event can be used in any context that permits a field. The field contains a delegate (§15) which refers to the list of event handlers that have been added to the event. If no event handlers have been added, the field contains null.

Thus, the reason you can't treat the Move event like a field is that it is defined in a different type (in this case, your superclass). I agree with @womp's speculation that the designers made this choice to prevent unintended monkeying with the event. It seems obviously bad to allow unrelated types (types not derived from the type declaring the event) to do this, but even for derived types, it might not be desirable. They probably would have had to include syntax to allow the event declaration to be made private or protected with respect to field-style usage, so my guess is that they opted to just disallow it entirely.

Charlie
+1  A: 

You only need the code you posted in the class where the event itself is defined. All derived classes should simply call OnShapeChanged() or OnMove() directly, without the copying etc., so you shouldn't be writing that code at all in your classes (since the Move event is defined in the base).

If you do need to do some kind of processing in the derived class (maybe you need to fiddle with your collection class?), you override the virtual OnXXX call and do you stuff before calling base.OnXXX(). In the MSDN article, the Circle class corresponds to your DockedToolWindow class. The same pattern should be available to your derived classes.

David Pope
I see that OnMove() would solve this, however how can't invoke OnMove() explicity when it's needed can I? I don't know where the framework actually calls the Move delegate, and is probably hidden from me anyway.
Balk
+2  A: 

You cannot directly fire base class events. This is exactly the reason why you had to make your OnShapeChanged method protected instead of private.

Use base.OnMove() instead.

Groo
I see that OnMove() would solve this, however how can't invoke OnMove() explicity when it's needed can I? I don't know where the framework actually calls the Move delegate, and is probably hidden from me anyway.
Balk
OnMove fires the Move event, in the same way as OnShapeChanged fires the ShapeChanged event in your code. It is a common pattern to add protected members which fire events, to make them visible to derived classes. In that case, the "On" prefix is usually added (OnMove, OnClick, etc.)
Groo
To see where the framework actually fires the Move event, use the Reflector tool (http://www.red-gate.com/products/reflector/) to get the source code for the Form.OnMove method.
Groo