I'm trying to better understand how Events and their handler work, but I don't understand why when raising an event it is usually preferred to raise an identical event, that our event itself.
To be more specific, when looking at msdn doc (https://msdn.microsoft.com/en-us/library/db0etb8x.aspx) it looks like that :
class Counter
{
private int threshold;
private int total;
public Counter(int passedThreshold)
{
threshold = passedThreshold;
}
public void Add(int x)
{
total += x;
if (total >= threshold)
{
ThresholdReachedEventArgs args = new ThresholdReachedEventArgs();
args.Threshold = threshold;
args.TimeReached = DateTime.Now;
OnThresholdReached(args);
}
}
protected virtual void OnThresholdReached(ThresholdReachedEventArgs e)
{
EventHandler<ThresholdReachedEventArgs> handler = ThresholdReached;
if (handler != null)
{
handler(this, e);
}
}
public event EventHandler<ThresholdReachedEventArgs> ThresholdReached;
}
What I don't understand is why "handler" is created in the OnThresholdReached function, instead of having
protected virtual void OnThresholdReached(ThresholdReachedEventArgs e)
{
if (ThresholdReached!= null)
{
ThresholdReached(this, e);
}
}
Why should we create this "handler" ?
Consider this code:
if (ThresholdReached!= null)
{
ThresholdReached(this, e);
}
What would happen in multi-threaded code if the handler for ThresholdReached is removed after if (ThresholdReached!= null), but before ThresholdReached(this, e); is called?
Taking a copy of the handler prevents this situation from occuring and makes the raising of the event thread-safe.
Related
I have a base class that contains the following events:
public event EventHandler Loading;
public event EventHandler Finished;
In a class that inherits from this base class I try to raise the event:
this.Loading(this, new EventHandler()); // All we care about is which object is loading.
I receive the following error:
The event 'BaseClass.Loading' can only appear on the left hand side of += or -= (BaseClass')
I am assuming I cannot access these events the same as other inherited members?
What you have to do , is this:
In your base class (where you have declared the events), create protected methods which can be used to raise the events:
public class MyClass
{
public event EventHandler Loading;
public event EventHandler Finished;
protected virtual void OnLoading(EventArgs e)
{
EventHandler handler = Loading;
if( handler != null )
{
handler(this, e);
}
}
protected virtual void OnFinished(EventArgs e)
{
EventHandler handler = Finished;
if( handler != null )
{
handler(this, e);
}
}
}
(Note that you should probably change those methods, in order to check whether you have to Invoke the eventhandler or not).
Then, in classes that inherit from this base class, you can just call the OnFinished or OnLoading methods to raise the events:
public AnotherClass : MyClass
{
public void DoSomeStuff()
{
...
OnLoading(EventArgs.Empty);
...
OnFinished(EventArgs.Empty);
}
}
You can only access an event in the declaring class, as .NET creates private instance variables behind the scenes that actually hold the delegate. Doing this..
public event EventHandler MyPropertyChanged;
is actually doing this;
private EventHandler myPropertyChangedDelegate;
public event EventHandler MyPropertyChanged
{
add { myPropertyChangedDelegate += value; }
remove { myPropertyChangedDelegate -= value; }
}
and doing this...
MyPropertyChanged(this, EventArgs.Empty);
is actually this...
myPropertyChangedDelegate(this, EventArgs.Empty);
So you can (obviously) only access the private delegate instance variable from within the declaring class.
The convention is to provide something like this in the declaring class..
protected virtual void OnMyPropertyChanged(EventArgs e)
{
EventHandler invoker = MyPropertyChanged;
if(invoker != null) invoker(this, e);
}
You can then call OnMyPropertyChanged(EventArgs.Empty) from anywhere in that class or below the inheritance heirarchy to invoke the event.
I am assuming I cannot access these events the same as other inherited members?
Precisely. It's customary to provide a protected function OnXyz or RaiseXyz for each event in the base class to enable raising from inherited classes. For example:
public event EventHandler Loading;
protected virtual void OnLoading() {
EventHandler handler = Loading;
if (handler != null)
handler(this, EventArgs.Empty);
}
Called in the inherited class:
OnLoading();
You can try this way, It works for me:
public delegate void MyEventHaldler(object sender, EventArgs e);
public class B
{
public virtual event MyEventHaldler MyEvent;
protected override void OnChanged(EventArgs e)
{
if (MyEvent != null)
MyEvent(this, e);
}
}
public class D : B
{
public override event MyEventHaldler MyEvent;
protected override void OnChanged(EventArgs e)
{
if (MyEvent != null)
MyEvent(this, e);
}
}
not to resurrect an old thread but in case anybody is looking, what I did was
protected EventHandler myPropertyChangedDelegate;
public event EventHandler MyPropertyChanged
{
add { myPropertyChangedDelegate += value; }
remove { myPropertyChangedDelegate -= value; }
}
This lets you inherit the event in a derived class so you can invoke it without requiring to wrap the method while keeping the += syntax. I guess you could still do that with the wrapping methods if you did
public event EventHandler MyPropertyChanged
{
add { AddDelegate(value); }
remove { RemoveDelegate(value); }
}
I have this event in a webservice:
public event FindProductsByCharacteristicsCompletedEventHandler FindProductsByCharacteristicsCompleted
{
[MethodImpl(MethodImplOptions.Synchronized)]
add
{
_findProductsByCharacteristicsCompleted += value;
}
[MethodImpl(MethodImplOptions.Synchronized)]
remove
{
_findProductsByCharacteristicsCompleted -= value;
}
}
And im then checking if the event value is null with this later in the class:
private void OnFindProductsByCharacteristicsOperationCompleted(object arg)
{
var handler = _findProductsByCharacteristicsCompleted;
if (handler == null)
return;
handler(this, new FindProductsByCharacteristicsCompletedEventArgs(completedEventArgs.Results, completedEventArgs.Error, completedEventArgs.Cancelled, completedEventArgs.UserState));
}
Your event implementation looks like it is an endless recursion. You are using the property itself in its implementation.
Change it to this:
private FindProductsByCharacteristicsCompletedEventHandler
_findProductsByCharacteristicsCompleted;
public event FindProductsByCharacteristicsCompletedEventHandler
FindProductsByCharacteristicsCompleted
{
[MethodImpl(MethodImplOptions.Synchronized)]
add
{
_findProductsByCharacteristicsCompleted += value;
}
[MethodImpl(MethodImplOptions.Synchronized)]
remove
{
_findProductsByCharacteristicsCompleted -= value;
}
}
And now, implement your method like this:
var handler = _findProductsByCharacteristicsCompleted;
if(handler == null)
return;
handler(this, new FindProductsByCharacteristicsCompletedEventArgs(...));
This has the advantage that it is thread-safe.
Even if someone detached the last handler from the event after you checked for null but before you actually raised the event, you would not get an exception, because you are operating on the unchanged local variable.
I have created a DLL using the following code. I have compiled this code as a DLL.
namespace DllEventTrigger
{
public class Trigger
{
public delegate void AlertEventHandler(Object sender, AlertEventArgs e);
public Trigger()
{
}
public void isRinging()
{
AlertEventArgs alertEventArgs = new AlertEventArgs();
alertEventArgs.uuiData = "Hello Damn World!!!";
CallAlert(new object(), alertEventArgs);
}
public event AlertEventHandler CallAlert;
}
public class AlertEventArgs : EventArgs
{
#region AlertEventArgs Properties
private string _uui = null;
#endregion
#region Get/Set Properties
public string uuiData
{
get { return _uui; }
set { _uui = value; }
}
#endregion
}
}
Now I'm trying to handle the event triggered by this dll in a forms application with this code.
namespace DLLTriggerReciever
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Trigger trigger = new Trigger();
trigger.isRinging();
trigger.CallAlert += new Trigger.AlertEventHandler(trigger_CallAlert);
}
void trigger_CallAlert(object sender, AlertEventArgs e)
{
label1.Text = e.uuiData;
}
}
}
My problem i'm not sure where i went wrong. Please suggest.
You need to assign your event handler before the event is actually fired, otherwise the code will throw a NullReferenceException.
trigger.CallAlert += new Trigger.AlertEventHandler(trigger_CallAlert);
trigger.isRinging();
Additionally, it's a recommended practice to check first, whether there are handlers assigned:
var handler = CallAlert; // local variable prevents a race condition to occur
if (handler != null)
{
handler(this, alertEventArgs);
}
as #Gene said, you need to register the event before raising it.
anyway, it's a good practice to check if someone is register to the event you're about to raise by checking for null.
like this:
if (this.CallAlert != null)
this.CallAlert(new object(), alertEventArgs);
I got this event handle and how can I do unit test for this
public class MyLearningEvent
{
private event EventHandler _Closed;
public event EventHandler Closed
{
add
{
_Closed -= value;
_Closed += value;
}
remove
{
_Closed -= value;
}
}
public void OnClosed()
{
if (_Closed != null) _Closed(this, EventArgs.Empty);
}
}
Just modified code so that much clear
Thanks
You should not unit test that code. It's a feature which is built into .NET. Your event handling is flawed imho.
add
{
_Closed -= value;
_Closed += value;
}
Probably means that your invokers don't keep track on if they have subscribed or not. That can lead to memory leaks: http://blog.naviso.fr/wordpress/wp-content/uploads/2011/11/MemoryLeaks-English.jpg
A more robust (and thread safe implementation) is:
public class MyLearningEvent
{
public event EventHandler Closed = delegate {};
public void TriggerClosed()
{
Closed(this, EventArgs.Empty);
}
}
But you should not let anyone else trigger that event (make the TriggerClosed private/protected)
Try this method. This assumes MyClass.Close() raises the MyClass.Closed event.
public void ClosedEventHandlerIsNotCalledAfterBeingRemoved()
{
MyLearningEvent Target = new MyLearningEvent();
EventHandler Target_Closed = new EventHandler((sender, e) => { Assert.Fail("Closed EventHandler was raised after being removed."); });
Target.Closed += Target_Closed;
Target.Closed -= Target_Closed;
Target.OnClosed();
}
I'm making custom events for C# and sometimes it isn't working.
This is how I'm making the event happen:
private bool isDoorOpen;
public bool IsDoorOpen {
get { return isDoorOpen;}
private set { isDoorOpen = value; DoorsChangeState(this, null);}
}
And these are the event declarations:
//events
public delegate void ChangedEventHandler(Elevator sender, EventArgs e);
public event ChangedEventHandler PositionChanged;
public event ChangedEventHandler DirectionChanged;
public event ChangedEventHandler BreaksChangeState;
public event ChangedEventHandler DoorsChangeState;
This works as long as there are methods attached to the events, but if there isn't, it throws a null ref exception. What am I doing wrong?
The recommended way to call an event is
var handler = this.DoorsChangeState;
if (handler != null)
handler(this, null);
The reason for copying the handler locally is incase the event handler changes on another thread while you're checking for null.
EDIT: Found the article talking about race conditions.
http://blogs.msdn.com/ericlippert/archive/2009/04/29/events-and-races.aspx
I know this question has been discussed (and answered) several times here on SO.
Also somewhere here i got the following extension methods to make this pattern more easy to use:
public static class EventHandlerExtensions
{
public static void FireEvent<T>(this EventHandler<T> handler, object sender, T args) where T : EventArgs
{
var temp = handler;
if (temp != null)
{
temp(sender, args);
}
}
public static void FireEvent(this EventHandler handler, object sender)
{
var temp = handler;
if (temp != null)
{
temp(sender, EventArgs.Empty);
}
}
}
So in your code you can say:
public bool IsDoorOpen
{
get { return isDoorOpen;}
private set
{
isDoorOpen = value;
DoorsChangeState.FireEvent(this);
}
}
If a event isn't subscribed to when it fires, a NullReferenceException will be thrown. This is correct behaviour, not something you've done wrong.
You should check:
if(DoorsChangeState != null)
{
DoorsChangeState(this, null); // Only fire if subscribed to
}
Before invoking an event you must check if the event is null:
if (DoorsChangeState != null)
DoorsChangeState(this, null);
When DoorsChangeState is null that means there are no listeners on that event.
You need to check to see if the event has been subscribed to.
I use this standard form for throwing all of my events.
var temp = EventName;
if(EventName!= null)
temp(this, null);