Why virtual event does not not call overridden handler - c#

Consider the following scenario, may be this scenario is very hypothetical
public delegate void MyDel();
public class T1
{
public T1()
{
}
public virtual event MyDel MyEvent;
public virtual void RaiseEvent()
{
MyEvent();
}
protected virtual void HandleEvent()
{
MessageBox.Show("base event");
}
}
public class T2:T1
{
public override event MyDel MyEvent;
public T2()
{
MyEvent += new MyDel(HandleEvent);
}
protected override void HandleEvent()
{
MessageBox.Show("overridden event");
}
}
and main client code
baseT = new T2();
baseT.MyEvent += new MyDel(() => MessageBox.Show("From client"));
baseT.RaiseEvent();
Why does it throw an exception, why virtual events do not behave like virtual/overridden methods?

The interface of an event is really just a pair of methods, add and remove. A private backing delegate is generated for auto-implemented events that is accessible only inside the declaring class through the event's name.
The virtual keyword for an event only applies to the add/remove method pair. Accessing and invocation of the backing delegate is not virtual for an auto-implemented event. When the subscribe occurs on an instance of the derived class (T2), it is using the overridden add/remove methods which use its own backing delegate. The base class's backing delegate is still null and is still being invoked in RaiseEvent. This causes a NullReferenceException when RaiseEvent is called.
Virtual events are kind of rare. I would probably make the event itself non-virtual and use protected virtual methods to allow the derived class to modify the event's behavior.

Related

Opennetcf.IOC event subscription and inheritance

Class1 has event with attribute [EventPublication("event1")].
Class2 and Class3 inherits from Class1.
I want to subscribe Method1 to event in object from Class2 and Method2 to event in object from Class3 using [EventSubscription].
But in the derived classes there is the same EventPublication name of the event. So how to distinguish events in derived classes? Is it possible?
EDIT:
Maybe I misunderstand some obvious things about IoC or I try to complicate simple solution...
I will try to clarify my question. Here is some code:
class BasePresenter
{
[EventPublication("event")]
public event Action action;
public void Run()
{
someAction();
if (action != null)
action();
}
protected virtual void someAction()
{
}
}
class Presenter1 : BasePresenter
{
protected override void someAction()
{
}
}
class Presenter2 : BasePresenter
{
protected override void someAction()
{
}
}
class AnotherClass
{
[EventSubscription("event", ThreadOption.Caller)]
public void action1()
{
System.Windows.Forms.MessageBox.Show("Presenter1 started");
}
[EventSubscription("event", ThreadOption.Caller)]
public void action2()
{
System.Windows.Forms.MessageBox.Show("Presenter2 started");
}
}
There is action1() and action2() methods in Another class. I would like to fire action1() when instance of Presenter1 Run() method is called and fire action2() when instance of Presenter2 Run() method is called. But calling Run() method will fire both methods action1 and action2.
I'm not certain I understand the question. There are two ends to the event aggregation, a Publisher and a Subscriber. They are "connected" by the string event name you use in the attribute and nothing else.
A subscription can be done in the same class as the publication, though it's not clear to me why you'd ever do that, just have the base class call a virtual method that the derived classes implement and you're done.
If you want to use events and you want to know if the event source instance is not the receiver instance, just check the event's source input parameter against this, something along these lines:
[EventSubscription("myevent")]
public void OnEvent(object sender, EventArgs a)
{
if(sender.Equals(this)) return;
// do stuff here - the event came from another class instance
}

Why do virtual field-like events work the way they do in C#? [duplicate]

This question already has answers here:
How virtual events work in C#?
(3 answers)
C#: What are virtual events and how can they be used?
(3 answers)
Closed 8 years ago.
Recently I discovered that virtual events don't work as one might expect in C#.
Consider this code:
public abstract class MyClassBase
{
public virtual event EventHandler<EventArgs> MyEvent;
public void DoStuff()
{
if (MyEvent != null)
{
MyEvent(this, EventArgs.Empty);
}
}
}
public class MyClassDerived : MyClassBase
{
public override event EventHandler<EventArgs> MyEvent;
}
Given this definition, the following code does not behave as I would expect:
MyClassBase obj = new MyClassDerived();
obj.MyEvent += (s, e) => { /* Never gets called */ };
// Call method on base class that raises MyEvent
obj.DoStuff();
I've done a bit more extensive write-up on my blog, but suffice it to say that I found a warning on MSDN that confirms that the behavior is unexpected, but it doesn't say anything about why. Can anyone think of a reason why it is implemented that way? Was it originally a bug which is left there for backward compatibility? I would imagine that some kind of compiler warning could at least have been added.
I realize this is most likely speculation, but I'm trying to find a plausible explanation for this weird behavior.
Field-like events are expanded by the compiler to something like this:
public abstract class MyClassBase
{
private EventHandler _myEvent;
public virtual event EventHandler MyEvent
{
add { _myEvent += value; }
remove { _myEvent -= value; }
}
public void DoStuff()
{
if (_myEvent != null)
{
_myEvent(this, EventArgs.Empty);
}
}
}
(notice how the DoStuff method uses the field, not the event)
So when you override the event with another field-like event, you get a new field, and the overridden event modifies the new field, instead of the one from the base class:
public class MyClassDerived : MyClassBase
{
private EventHandler _myEvent;
public override event EventHandler MyEvent
{
add { _myEvent += value; }
remove { _myEvent -= value; }
}
}
So when you add a handler to an instance of MyClassDerived, the _myEvent field in MyClassBase isn't affected, so the DoStuff method sees a null handler.

How can I have a "generic" delegate?

My current situation looks like this:
I have these two delegates in a separate file:
public delegate void EventHandler(Object obj, EventArgs e);
public delegate void OtherEventHandler(Object obj, OtherEventArgs e);
I have an Event class that look like this:
class Event {
EventHandler _handler;
public Event(EventHandler handler) {
_handler = handler;
}
}
I have another class that inherits Event this way:
class OtherEvent : Event {
OtherEventHandler _handler;
public OtherEvent (OtherEventHandler handler) : base(handler) {
_handler = handler;
}
}
This one is where the problem occurs. The error is with the part on base(handler). Because handler in OtherEvent is an OtherEventHandler, the base class cannot accept it; the base class only accepts EventHandler.
My intention is to have a "generic" delegate such that when OtherEvent inherits the methods from Event, OtherEvent still has the flexibility to use its own type of delegate with different parameters from its parent for the handler class field.
What can I do to achieve my intention?
Whats wrong with usage of default delegate EventHandler<TEventArgs>? It can be parametrized with any type, inherited from EventArgs.
Also I don't really understand purpose of Event class. You can define events simply this way:
public event EventHandler<OtherEventArgs> OtherEvent;
Perhaps use a generic instead of inheritance?
class Event<TEvent>
{
TEvent _handler;
public Event(TEvent handler)
{
_handler = handler;
}
}

Public event of base class in derived class

I have a base-class (let it be SomeBaseClass) containing a public event (SomeEvent) and I have a derived-class in which I want to raise this event but I can't(!!) VS 2010 says me (in derived-class in line: base.SomeEvent != null) "The event 'SomeBaseClass.SomeEvent' can only appear on the left hand side of += or -=". If I replace base on this It is make no sense.
No, it's absolutely right - the event is only an event (with subscription and unsubscription) as far as a derived class is concerned. If your base class wants to let derived classes raise the event, it should include a protected method to do so (typically a virtual OnFoo(EventHandler) for an event called Foo with the EventHandler type, for example). Note that if you write a field-like event in C# like this:
public event EventHandler Foo;
That's actually declaring a private field called Foo (which that class and any nested classes have access to) and a public event (which consists only of subscribe/unsubscribe). You could declare your own "custom" event like this:
protected EventHandler foo;
// Note: not thread-safe. Only present for demonstration purposes.
public event EventHandler Foo
{
add { foo += value; }
remove { foo -= value; }
}
and then derived classes would have access to the field... but I wouldn't recommend that. (I rarely declare non-private fields, other than for constants.)
You need to do it the right way (i.e., the idiomatic way in C#)
public class Base {
public event EventHandler<EventArgs> SomeEvent;
protected virtual void OnSomeEvent(EventArgs e) {
EventHandler<EventArgs> handler = SomeEvent;
if (handler != null) {
handler(this, e);
}
}
}
public class Derived {
protected virtual void OnSomeEvent(EventArgs e) {
// derived event handling here
// then invoke the base handler
base.OnSomeEvent(e);
}
}
The reason that you do it like this is because events can only be invoked from within the defining class.

Why events can't be used in the same way in derived classes as in the base class in C#?

In following code, I want to extend the behaviour of a class by deriving/subclassing it, and make use of an event of the base class:
public class A
{
public event EventHandler SomeEvent;
public void someMethod()
{
if(SomeEvent != null) SomeEvent(this, someArgs);
}
}
public class B : A
{
public void someOtherMethod()
{
if(SomeEvent != null) SomeEvent(this, someArgs); // << why is this not possible?
//Error: The event 'SomeEvent' can only appear on the left hand side of += or -=
//(except when used from within the type 'A')
}
}
Why isn't it possible?
And what is the common solution for this kind of situation?
Others have explained how to get round the issue, but not why it's coming up.
When you declare a public field-like event, the compiler creates a public event, and a private field. Within the same class (or nested classes) you can get at the field directly, e.g. to invoke all the handlers. From other classes, you only see the event, which only allows subscription and unsubscription.
The standard practice here is to have a protected virtual method OnSomeEvent on your base class, then call that method in derived classes. Also, for threading reasons you will want to keep a reference to the handler before checking null and calling it.
For an explanation of the why read Jon Skeet's answer or the C# specification which describes how the compiler automatically creates a private field.
Here is one possible work around.
public class A
{
public event EventHandler SomeEvent;
public void someMethod()
{
OnSomeEvent();
}
protected void OnSomeEvent()
{
EventHandler handler = SomeEvent;
if(handler != null)
handler(this, someArgs);
}
}
public class B : A
{
public void someOtherMethod()
{
OnSomeEvent();
}
}
Edit: Updated code based upon Framework Design Guidelines section 5.4 and reminders by others.
Todd's answer is correct. Often you will see this implemented throughout the .NET framework as OnXXX(EventArgs) methods:
public class Foo
{
public event EventHandler Click;
protected virtual void OnClick(EventArgs e)
{
var click = Click;
if (click != null)
click(this, e);
}
}
I strongly encourage you to consider the EventArgs<T>/EventHandler<T> pattern before you find yourself making all manner of CustomEventArgs/CustomEventHandler for raising events.
The reason the original code doesn't work is because you need to have access to the event's delegate in order to raise it, and C# keeps this delegate private.
Events in C# are represented publicly by a pair of methods, add_SomeEvent and remove_SomeEvent, which is why you can subscribe to an event from outside the class, but not raise it.
My answer would be that you shouldn't have to do this.
C# nicely enforces Only the type declaring/publishing the event should fire/raise it.
If the base class trusted derivations to have the capability to raise its events, the creator would expose protected methods to do that. If they don't exist, its a good hint that you probably shouldn't do this.
My contrived example as to how different the world would be if derived types were allowed to raise events in their ancestors. Note: this is not valid C# code.. (yet..)
public class GoodVigilante
{
public event EventHandler LaunchMissiles;
public void Evaluate()
{
Action a = DetermineCourseOfAction(); // method that evaluates every possible
// non-violent solution before resorting to 'Unleashing the fury'
if (null != a)
{ a.Do(); }
else
{ if (null != LaunchMissiles) LaunchMissiles(this, EventArgs.Empty); }
}
virtual protected string WhatsTheTime()
{ return DateTime.Now.ToString(); }
....
}
public class TriggerHappy : GoodVigilante
{
protected override string WhatsTheTime()
{
if (null != LaunchMissiles) LaunchMissiles(this, EventArgs.Empty);
}
}
// client code
GoodVigilante a = new GoodVigilante();
a.LaunchMissiles += new EventHandler(FireAway);
GoodVigilante b = new TriggerHappy(); // rogue/imposter
b.LaunchMissiles += new EventHandler(FireAway);
private void FireAway(object sender, EventArgs e)
{
// nuke 'em
}
Wrap it with a protected virtual On... method:
public class BaseClass
{
public event EventHandler<MyArgs> SomeEvent;
protected virtual void OnSomeEvent()
{
if(SomeEvent!= null)
SomeEvent(this, new MyArgs(...) );
}
}
Then override this in a derived class
public class DerivedClass : BaseClass
{
protected override void OnSomeEvent()
{
//do something
base.OnSomeEvent();
}
}
You'll set this pattern all over .Net - all form and web controls follow it.
Do not use the prefix Raise... - this is not consistent with MS's standards and can cause confusion elsewhere.

Categories

Resources