Clearing or checking for event handlers in C# - c#

I have some C# code that updates some properties of an object. I have an event handler defined to help me respond when the update process is done. Unfortunately, I've learned that this event is getting fired multiple times. I suspect this is happening because the event handler is being set at the wrong time. Currently, it is being set like the following:
myObject.Update_Succeeded += new EventHandler(myObject_Update_Succeeded);
Due to the complexity of the code, I'm having a difficult time of tracking down where it should be set. I would like to only set the event handler it hasn't been previously set. Because of this, I want to do something like this:
ClearEventHandlers(myObject);
or
myObject.Update_Succeeded = null;
myObject.Update_Succeeded += new EventHandler(myObject_Update_Succeeded);
Is there a way to accomplish what I'm trying?
Thank you!

Yes, you can customize the add/remove accessors of your event. This article describes these accessors. But you can do something like:
class MyClass
{
private EventHandler _myEvent;
public event EventHandler MyEvent
{
[MethodImpl(MethodImplOptions.Synchronized)]
add
{
_myEvent = (EventHandler)Delegate.Combine(_myEvent, value);
}
[MethodImpl(MethodImplOptions.Synchronized)]
remove
{
_myEvent = (EventHandler)Delegate.Remove(_myEvent, value);
}
}
public void ClearMyEvent() {
_myEvent = null;
}
...
}

You should be able to remove a handler using the subtract operator like below
myObject.Update_Succeeded -= new EventHandler(myObject_Update_Succeeded);
Or check this out for a way to remove all event handler if you are in doubt
How to remove all event handlers from a control

Proper way should be to detach the handler from each event after you no longer use it:
public class MyObjectListener
{
private readonly MyObject _object;
public class MyObjectListener(MyObject obj)
{
_object = obj;
Attach();
}
// adds event handlers
private void Attach()
{
obj.UpdateSucceeded += UpdateSuceededHandler;
obj.UpdateFailed += UpdateFailedHandler;
}
// removes event handlers
private void Detach()
{
obj.UpdateSucceeded -= UpdateSuceededHandler;
obj.UpdateFailed -= UpdateFailedHandler;
}
...
}
The only thing you need to decide is where to call the Detach method. For example, you can call it in the handler itself:
private void UpdateSuceededHandler(object sender, Data args)
{
Detach();
// do something when it succeeds
}
private void UpdateFailedHandler(object sender, Data args)
{
Detach();
// do something when it fails
}
Or, you could allow users of MyObjectListener to tell it that it no longer needs to listen to the attached object:
public void StopListening()
{
Detach();
}
An object which raises an event should not allow its listeners to modify the event invocation list. Each event listener should subscribe or unsubscribe its own event handlers only.

You better set event handler on the initialization of your object i.e. in your Constructor.

Related

How to pass an event through classes in C#

Here are 3 C# classes :
class StartClass
{
event StartEvent;
// some code
class MidClass
{
private StartClass _startClass;
public MidClass (StartClass startClass)
{
_startClass = startClass;
}
// some code
class EndClass
{
private MidClass _midClass;
public EndClass (MidClass midClass)
{
_midClass = midClass;
}
// some code
public void OnStartEvent ()
{
// code to be executed on StartEvent called
}
What's best way if you want to attach EndClass.OnStartEvent listener to StartClass.StartEvent event ?
I think the best way would be to create a MidClass.StartEvent property referencing StartClass.StartEvent event in order to be able to attach the listener in EndClass doing _midClass.StartEvent =+ OnStartEvent;. Am I right ? How to attach StartClass.StartEvent to MidClass.StartEvent ?
In C# an event is much like a property. It is a wrapper for a delegate. Usually we are using auto-implemented events. But we can expand them. Properties have get and set accessors. Events have add and remove accessors.
class MidClass
{
private readonly StartClass _startClass;
public MidClass (StartClass startClass)
{
_startClass = startClass;
}
public event EventHandler StartEvent
{
add => _startClass.StartEvent += value;
remove => _startClass.StartEvent -= value;
}
}
Here, we create a StartEvent in MidClass that is a wrapper for the corresponding event in StartClass. StartClass.StartEvent must be public.
The advantage of this approach is that an event handler subscribing to MidClass.StartEvent will directly be attached to StartClass.StartEvent, with no intermediate call occurring when the event is risen.
One word to naming. Methods named OnEventName are usually used to raise events, while methods named PublisherName_EventName are used for event handlers.
class StartClass
{
public event EventHandler StartEvent;
private virtual void OnStartEvent()
{
StartEvent?Invoke(this, EventArgs.Empty);
}
}
class EndClass
{
private MidClass _midClass;
public EndClass (MidClass midClass)
{
_midClass = midClass;
_midClass.StartEvent += MidClass_StartEvent;
}
private void MidClass_StartEvent (object sender, EventArgs e)
{
// Code to be executed when StartEvent is triggered
}
}
I think the best way would be to create a MidClass.StartEvent property referencing StartClass.StartEvent event in order to be able to attach the listener in EndClass doing _midClass.StartEvent =+ OnStartEvent;. Am I right?
If you want to keep the reference to StartClass a private implementation detail of MidClass: Yes, implementing a "proxy" StartEvent in MidClass is the right way to do it.
How to attach StartClass.StartEvent to MidClass.StartEvent ?
By attaching a listener to _startClass.StartEvent which just raises the corresponding event in MidClass:
class MidClass
{
public event EventHandler StartEvent;
private readonly StartClass _startClass;
public MidClass(StartClass startClass)
{
_startClass = startClass;
_startClass.StartEvent += (sender, e) => this.StartEvent?.Invoke(this, e);
}
...
}
Note that I also added the readonly modifier to _startClass: If the value of _startClass changes during the lifetime of MidClass, you need to detach your event handler from the old reference and attach it to the new reference.

Custom Event handler is getting called twice?

I've created an event handler that simply returns a list of objects that I receive from a web service when the call completes.
Now I went ahead and ran the app in debug mode and found out that the first time the event is called it works perfectly, but immediately after it completes the event is being fired for a second time. I've checked and am absolutely sure I am not calling the event more than once in the receiver class.
This is my first shot at creating custom event handlers inside my applications so I am not entirely sure the implementation is 100% accurate.
Any ideas of what might be causing this? Is the way I created the event handler accurate?
This is the DataHelper class
public class DataHelper
{
public delegate void DataCalledEventHandler(object sender, List<DataItem> dateItemList);
public event DataCalledEventHandler DataCalled;
public DataHelper()
{
}
public void CallData()
{
List<DataItem> dataItems = new List<DataItem>();
//SOME CODE THAT RETURNS DATA
DataCalled(this, dataItems);
}
}
This is where I subscribed to my event:
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
GetNewDataItems();
}
private void GetNewDataItems()
{
try
{
DataHelper dataHelper = new DataHelper();
dataHelper.CallData();
dataHelper.DataCalled += new DataHelper.DataCalledEventHandler(dataHelper_DataCalled);
}
catch
{
//Handle any errors
}
}
}
void dataHelper_DataCalled(object sender, List<DataItem> dataItemsList)
{
//Do something with results
//NOTE: THIS IS WHERE THE EXCEPTION OCCURS WHEN EVENT IS FIRED FOR SECOND TIME
}
Probably you added the delegate twice, is it possible?
In this case the problem is not in who calls the delegate but in who adds the delegate to the event.
Probably you did something like...
private Class1 instance1;
void callback(...)
{
}
void myfunction()
{
this.instance1.DataCalled += this.callback;
this.instance1.DataCalled += this.callback;
}
If not, try to add a breakpoint where you subscribe to the event and see if it is called twice.
As a side note, you should always check for null when calling an event, if there is no subscriber you can get a NullReferenceException.
I would also suggest you to use a variable to store the event delegate to avoid the risk of multithreading failure.
public void CallData()
{
List<DataItem> dataItems = new List<DataItem>();
var handler = this.DataCalled;
if (handler != null)
handler(this, dataItems);
}
Edit: since now I see the code, is obvious that each time you call the GetNewDataItems method you are subsribing every time to the event.
Do in such a way you subscribe only once, for example, in constructor, or store your variable somewhere or deregister the event when you finish.
This code contains also a probable memory leak: every time you add a delegate you keep alive both the instance that contains the event and the instance that contains the subscribed method, at least, until both are unreferenced.
You can try to do something like this...
void dataHelper_DataCalled(object sender, List<DataItem> dataItemsList)
{
// Deregister the event...
(sender as Class1).DataCalled -= dataHelper_DataCalled;
//Do something with results
}
In this way however you must ensure that if there is not an exception during the event registration the event will be fired or you have again memory leaks.
Instead of an event perhaps you need just a delegate. Of course you should set your delegate field to null when you want to release the delegate.
// in data helper class
private DataHelper.DataCalledEventHandler myFunctor;
public void CallData(DataHelper.DataCalledEventHandler functor)
{
this.myFunctor = functor;
//SOME CODE THAT RETURNS DATA
}
// when the call completes, asynchronously...
private void WhenTheCallCompletes()
{
var functor = this.myFunctor;
if (functor != null)
{
this.myFunctor = null;
List<DataItem> dataItems = new List<DataItem>();
functor(this, dataItems);
}
}
    
// in your function
...    dataHelper.CallData(this.dataHelper_DataCalled);    ...
The below lines on your code should be flipped. That is
These lines
dataHelper.CallData();
dataHelper.DataCalled += new DataHelper.DataCalledEventHandler(dataHelper_DataCalled);
Should be:
dataHelper.DataCalled += new DataHelper.DataCalledEventHandler(dataHelper_DataCalled);
dataHelper.CallData();
Because you first need to attach the event handler and then call other methods on the object which can raise the event

C# pattern to prevent an event handler hooked twice [duplicate]

This question already has answers here:
How to ensure an event is only subscribed to once
(8 answers)
Closed 8 years ago.
Duplicate of: How to ensure an event is only subscribed to once
and Has an event handler already been added?
I have a singleton that provides some service and my classes hook into some events on it, sometimes a class is hooking twice to the event and then gets called twice.
I'm looking for a classical way to prevent this from happening. somehow I need to check if I've already hooked to this event...
How about just removing the event first with -= , if it is not found an exception is not thrown
/// -= Removes the event if it has been already added, this prevents multiple firing of the event
((System.Windows.Forms.WebBrowser)sender).Document.Click -= new System.Windows.Forms.HtmlElementEventHandler(testii);
((System.Windows.Forms.WebBrowser)sender).Document.Click += new System.Windows.Forms.HtmlElementEventHandler(testii);
Explicitly implement the event and check the invocation list. You'll also need to check for null:
using System.Linq; // Required for the .Contains call below:
...
private EventHandler foo;
public event EventHandler Foo
{
add
{
if (foo == null || !foo.GetInvocationList().Contains(value))
{
foo += value;
}
}
remove
{
foo -= value;
}
}
Using the code above, if a caller subscribes to the event multiple times, it will simply be ignored.
I've tested each solution and the best one (considering performance) is:
private EventHandler _foo;
public event EventHandler Foo {
add {
_foo -= value;
_foo += value;
}
remove {
_foo -= value;
}
}
No Linq using required. No need to check for null before cancelling a subscription (see MS EventHandler for details). No need to remember to do the unsubscription everywhere.
You really should handle this at the sink level and not the source level. That is, don't prescribe event handler logic at the event source - leave that to the handlers (the sinks) themselves.
As the developer of a service, who are you to say that sinks can only register once? What if they want to register twice for some reason? And if you are trying to correct bugs in the sinks by modifying the source, it's again a good reason for correcting these issues at the sink-level.
I'm sure you have your reasons; an event source for which duplicate sinks are illegal is not unfathomable. But perhaps you should consider an alternate architecture that leaves the semantics of an event intact.
You need to implement the add and remove accessors on the event, and then check the target list of the delegate, or store the targets in a list.
In the add method, you can use the Delegate.GetInvocationList method to obtain a list of the targets already added to the delegate.
Since delegates are defined to compare equal if they're linked to the same method on the same target object, you could probably run through that list and compare, and if you find none that compares equal, you add the new one.
Here's sample code, compile as console application:
using System;
using System.Linq;
namespace DemoApp
{
public class TestClass
{
private EventHandler _Test;
public event EventHandler Test
{
add
{
if (_Test == null || !_Test.GetInvocationList().Contains(value))
_Test += value;
}
remove
{
_Test -= value;
}
}
public void OnTest()
{
if (_Test != null)
_Test(this, EventArgs.Empty);
}
}
class Program
{
static void Main()
{
TestClass tc = new TestClass();
tc.Test += tc_Test;
tc.Test += tc_Test;
tc.OnTest();
Console.In.ReadLine();
}
static void tc_Test(object sender, EventArgs e)
{
Console.Out.WriteLine("tc_Test called");
}
}
}
Output:
tc_Test called
(ie. only once)
Microsoft's Reactive Extensions (Rx) framework can also be used to do "subscribe only once".
Given a mouse event foo.Clicked, here's how to subscribe and receive only a single invocation:
Observable.FromEvent<MouseEventArgs>(foo, nameof(foo.Clicked))
.Take(1)
.Subscribe(MyHandler);
...
private void MyHandler(IEvent<MouseEventArgs> eventInfo)
{
// This will be called just once!
var sender = eventInfo.Sender;
var args = eventInfo.EventArgs;
}
In addition to providing "subscribe once" functionality, the RX approach offers the ability to compose events together or filter events. It's quite nifty.
Create an Action instead of an event. Your class may look like:
public class MyClass
{
// sender arguments <----- Use this action instead of an event
public Action<object, EventArgs> OnSomeEventOccured;
public void SomeMethod()
{
if(OnSomeEventOccured!=null)
OnSomeEventOccured(this, null);
}
}
have your singleton object check it's list of who it notifies and only call once if duplicated. Alternatively if possible reject event attachment request.
In silverlight you need to say e.Handled = true; in the event code.
void image_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
e.Handled = true; //this fixes the double event fire problem.
string name = (e.OriginalSource as Image).Tag.ToString();
DoSomething(name);
}
Please tick me if this helps.

Launch an event that has accessors

How can I launch an event that has accessors like this :
public event EventHandler CanExecuteChanged
{
add
{
CommandManager.RequerySuggested += value;
}
remove
{
CommandManager.RequerySuggested -= value;
}
}
If it were a normal event I would launch it by:
CanExecuteChanged(sender, EventArgs..).
But here it doesn't work - I can only do
CanExecuteChanged +=..
to attach a method do the event - but I can't Launch it.
Also some documentation on the subject would be appreciated.
Thanks.
EDIT
The event is from class implementing ICommand in WPF. there's nothing more to show :).
And no - the CommandManager.RequerySuggested(this, EventArgs.Empty); doesn't work.
EDIT2 Not sure what to say - Jon's example should have worked yet even if the add method is called correctly - when I try to call the event - it's null :|. I probably will
drop events with accessors.
I think you have events confused with delegates. Only the class exposing the event can raise it... Others can only subscribe-unsubscribe to it. If you are invoking the event from within the class declaring the event, it should work like a regular delegate.
The best page I could find on Events vs Delegates. Read up..
Can you post a bigger snippet.. something seems amiss..
Killer Update
I think I finally see your problem and how to solve it.
Short Answer: It does not know the name of delegate to invoke if you write your own accessors. If you don't.. the compiler adds the private delegate of known name and hence is able to invoke it
This code snippet shows what I mean. This MSDN article showed me the light. Great question dude.. I lost 30 mins. Upvoted :)
public class Hash1
{
private EventHandler myHomeMadeDelegate;
public event EventHandler FancyEvent
{
add
{
//myDelegate += value;
myHomeMadeDelegate = (EventHandler)Delegate.Combine(myHomeMadeDelegate, value);
}
remove
{
//myDelegate -= value;
myHomeMadeDelegate = (EventHandler)Delegate.Remove(myHomeMadeDelegate, value);
}
}
public event EventHandler PlainEvent;
public Hash1()
{
FancyEvent += new EventHandler(On_Hash1_FancyEvent);
PlainEvent += new EventHandler(On_Hash1_PlainEvent);
// FancyEvent(this, EventArgs.Empty); //won't work:What is the backing delegate called? I don't know
myHomeMadeDelegate(this, EventArgs.Empty); // Aha!
PlainEvent(this, EventArgs.Empty);
}
void On_Hash1_PlainEvent(object sender, EventArgs e)
{
Console.WriteLine("Bang Bang!");
}
void On_Hash1_FancyEvent(object sender, EventArgs e)
{
Console.WriteLine("Bang!");
}
}
That event is just subscribing to and unsubscribing from another event. If you want your subscribers (and only your subscribers - not separate ones to the other event) to be invoked, you'll need to keep hold of your subscribers separately. For instance, you could change the code to something like:
private EventHandler canExecuteChanged;
public event EventHandler CanExecuteChanged
{
add
{
CommandManager.RequerySuggested += value;
canExecuteChanged += value;
}
remove
{
CommandManager.RequerySuggested -= value;
canExecuteChanged -= value;
}
}
Ok, I found that if I want to trigger that event you have to do :
CommandManager.InvalidateRequerySuggested();.
You've got to invoke the underlying events directly. In your case, it looks as though this would be:
<blockquote>CommandManager.RequerySuggested(sender, EventArgs.…)</blockquote>
/EDIT: Ok, I didn't notice that CommandManager is a framework class. In this case, you obviously don't want to do what I've proposed. Jon's solution is to the point: You've got to keep track of your own event and invoke that (e.g. as a delegate). In keeping with Jon's example, invocation would look like this:
canExecuteChanged(sender, EventArgs.Empty);
wow, just had similar problems. The answer that helped me understand is somewhat like Gishu's.
Also from the C# specs, http://www.microsoft.com/en-us/download/details.aspx?id=7029, under "10.8.1 Field-like events" it says "When compiling a field-like event, the compiler automatically creates storage to hold the delegate,"
specs also says:
Thus, an instance event declaration of the form:
class X
{
public event D Ev;
}
could be compiled to something equivalent to:
class X
{
private D __Ev; // field to hold the delegate
public event D Ev {
add {
lock(this) { __Ev = __Ev + value; }
}
remove {
lock(this) { __Ev = __Ev - value; }
}
}
}
If you do something like the code below, the compiler compiles it successfully:
namespace ConsoleApplication1
{
class Program
{
public event EventHandler ss;
Program()
{
if (null != ss)
{
ss(this, EventArgs.Empty) ;
}
}
static void Main(string[] args)
{
new Program();
}
}
}
And if you add accessors to ss above, it will NOT compile:
namespace ConsoleApplication1
{
class Program
{
public event EventHandler ss
{
add { }
remove { }
}
Program()
{
if (null != ss)
{
ss(this, EventArgs.Empty) ;
}
}
static void Main(string[] args)
{
new Program();
}
}
}
There are two kinds of events demonstrated here
Field-like events => we can invoke
events with accessors => we cannot invoke (cannot find this in specs why, maybe I missed it)(and was only testing this on Visual Studio 2005, and the specs was the latest, I guess)

How to raise custom event from a Static Class

I have a static class that I would like to raise an event as part of a try catch block within a static method of that class.
For example in this method I would like to raise a custom event in the catch.
public static void saveMyMessage(String message)
{
try
{
//Do Database stuff
}
catch (Exception e)
{
//Raise custom event here
}
}
Thank you.
Important: be very careful about subscribing to a static event from instances. Static-to-static is fine, but a subscription from a static event to an instance handler is a great (read: very dangerous) way to keep that instance alive forever. GC will see the link, and will not collect the instance unless you unsubscribe (or use something like a WeakReference).
The pattern for creating static events is the same as instance events, just with static:
public static event EventHandler SomeEvent;
To make life easier (re null checking), a useful trick here is to add a trivial handler:
public static event EventHandler SomeEvent = delegate {};
Then you can simply invoke it without the null-check:
SomeEvent(null, EventArgs.Empty);
Note that because delegate instances are immutable, and de-referencing is thread-safe, there is never a race condition here, and no need to lock... who-ever is subscribed when we de-reference gets invoked.
(adjust for your own event-args etc).
This trick applies equally to instance events.
Your event would also need to be static:
public class ErrorEventArgs : EventArgs
{
private Exception error;
private string message;
public ErrorEventArgs(Exception ex, string msg)
{
error = ex;
message = msg;
}
public Exception Error
{
get { return error; }
}
public string Message
{
get { return message; }
}
}
public static class Service
{
public static EventHandler<ErrorEventArgs> OnError;
public static void SaveMyMessage(String message)
{
EventHandler<ErrorEventArgs> errorEvent = OnError;
if (errorEvent != null)
{
errorEvent(null, new ErrorEventArgs(null, message));
}
}
}
And Usage:
public class Test
{
public void OnError(object sender, ErrorEventArgs args)
{
Console.WriteLine(args.Message);
}
}
Test t = new Test();
Service.OnError += t.OnError;
Service.SaveMyMessage("Test message");
Several folks have offered up code examples, just don't fire an event using code such as:
if(null != ExampleEvent)
{
ExampleEvent(/* put parameters here, for events: sender, eventArgs */);
}
as this contains a race condition between when you check the event for null and when you actually fire the event. Instead use a simple variation:
MyEvent exampleEventCopy = ExampleEvent;
if(null != exampleEventCopy)
{
exampleEventCopy(/* put parameters here, for events: sender, eventArgs */);
}
This will copy any event subscribers into the exampleEventCopy, which you can then use as a local-only version of the public event without having to worry about any race conditions (Essentially, it is possible that another thread could pre-empt you right after you have checked the public event for null and proceed to remove all subscribers from the event, causing the subsequent firing of the event to throw an exception, by using a local-only copy, you avoid the possibility of another thread removing subscribers, since there is no way they could access the local variable).
Note: VS2008, C#
Just declare an event as you normally would within the static class, but be sure to mark the event as static:
public static event EventHandler Work;
Then just subscribe to it as you normally would.
Just to add "Delegates are immutable" So, as shown in the example above the following line obtains a copy of the delegate.
EventHandler<ErrorEventArgs> errorEvent = OnError;
The way I did this is the following:
1- define a delegate (this will enable you to have customized arguments):
public delegate void CustomeEventHandler(string str);
2- define an event based on the previously defined delegate:
public static event CustomeEventHandler ReadLine;
3- create an event handler:
static void OnLineRead(string currentLine)
{
if (ReadLine != null)
ReadLine(currentLine);
}
4- raise your event using the event handler (just call it wherever you want the event to be raised).

Categories