I have a ugly piece of code that adds event handlers. The problem is, if the code is called multiple times, the event handlers are called multiple times.
To solve the problem, I remove the event handler first and then add it.
Now I've seen the following behaviour:
Some event handlers can be checked like:
if (object.event == null) {
//
// Code
//
}
others of the form
if (object.object.event == null) {
//
// Code
//
}
I get a message like 'object.object.event' may only occur left of -= or +=.
(Since I'm using a german version of visual studio, I don't know the correct translation to english).
I have no idea why the behaviour looks this inconsequent so I would be grateful for some information on this.
To be more specific: It's user control.
if (myControl.Event == null) {
//
// works
//
}
if (myControl.TreeView.NodeMouseClick == null) {
//
// doesn't work
//
}
To solve the problem, I remove the event handler first and then add it.
That doesn't solve the problem. The event keyword provides accessors for a delegate object. Much like a property provides accessors for a field. On a property, you always need one get or set. An event has the add, remove and raise accessors. But the compiler will generate a default implementation for them if you don't do so yourself. Which is fairly common.
The advantage of a property accessor is that the backing field can be private. Nobody can mess with it, except the class that contains the field. All access has to go through the get and set accessors. The event keyword works exactly the same way, nobody can mess with the delegate object, except the code in the class that contains the event.
Which shoots a gaping hole in your attempt to avoid raising the event. You cannot mess with the list of subscribers for an event that's declared in another class, the compiler is telling you this. The normal way this is done is setting a bool flag to indicate that events have to be temporarily ignored. The event handler can check that flag and avoid executing any side-effects.
SLaks is correct, and has linked to some excellent resources. Here's a relevant quote from Chris Burrows' blog article:
Let me take a quick detour here and explain to you how the binding of += works in C#. There are two possibilities:
either there is an actual + operator, such as with ints, and x += y binds to “x = x + y” except that x is only evaluated once. This is the compound assignment operator; or
the thing on the left hand side is an event, and x.E += y binds to “x.add_E(y)”. This is the event accessor operator, and in fact this is the only way to bind to an event accessor.
So what do we have in the snippet above? Well, the extra bit of detail that you need to decide is the following rule about field-like events in C#: outside of the class or struct that defines a field-like event E, binding to the name E resolves to the event itself, on which the only legal operation is calling an accessor; inside the class or struct that defines a field-like event E, binding to the name E resolves to the private delegate field.
In your case, when resolving myControl.Event, you're inside the myControl class, so you don't see an event object; instead you see an actual delegate object, which you can compare with null. When resolving myControl.TreeView.NodeMouseClick, you're outside the TreeView class, so you can't access the actual delegate object; all you get is the event object, which cannot be compared to null.
If I understand correctly, all of this wouldn't help you anyway, since presumably after you check for null, you're going to try to fire the TreeView's event for it, which you can't do.
Depending on what you're trying to do, you could probably subclass TreeView and add an internal method that would call the protected TreeView.OnNodeMouseClick method to fire the event.
You can only access the backing field for an event defined in your class.
For more information, see the spec. (Although this has changed in C# 4, the changes are irrelevant to you)
Best practice in your case would be to create a protected internal OnEventName method in each class.
You can only query your own eventhandlers for attached listeners.
Automatic events, like this one:
public event EventHandler SomethingHappened;
are implemented by the compiler using a multicast delegate.
When you write myControl.Event == null, the compiler actually needs to call Delegate.GetInvocationList on that delegate. The compiler does not let you do that unless the code is inside a method of the class exposing the event, hence the error (it only allows you to add or remove from the invocation list).
If we were talking about an event you define in your own class, then you would have the option of exposing the invocation list (e.g. through a method) and doing what you are trying to do. But for existing classes (e.g. TreeView) it is not possible.
Related
Related to this question,
Check if an event already exists
but the difference is I just want to know if a particular method is attached to the event. So there may be other methods attached, but I just want to know if a particular one exists.
My environment is C# in dotnet 4.0.
E.g.
Event += MyMethod1;
Event += MyMethod2;
// Some code
if (MyMethod1IsAttachedToEvent())
{
// Achieved goal
}
Is this possible?
No. You cannot.
The event keyword was explicitly invented to prevent you from doing what you want to do. It makes the delegate object for the event inaccessible so nobody can mess with the events handlers.
Source : How to dermine if an event is already subscribed
Late answer here. I believe Parimal Raj answer is correct, as I could not find a way to directly access the events. However, here are two methods I created to get around this:
Delete before adding. If the method isn't there, I did not receive an error trying to delete the nonexistant method. This way you can insure the invocation list calls method1 only once.
Event -= MyMethod1;
Event += MyMethod1;
The objects you are adding an event to may have a .Tag property. You can use the Tag to store info about the methods you already added. Here I only have one method, so I just need to check if Tag is null. But more complicated scenarios can be handled this way:
if(control.Tag == null)
{
//ony added once, when tag is null
control.TextChanged += new EventHandler(validate);
control.Tag = new ControlTag();
}
Event.GetInvocationList().Any(x => x.Method.Name.Equals("yourmethodname"));
foreach ( Delegate existingHandler in this.EventHandler.GetInvocationList() )
{
if ( existingHandler == prospectiveHandler )
{
return true;
}
}
loop through the delegates using the GetInvocationList method.
Is there anything in c# that you can use to create a collection of events like lists, hashsets, and a dictionary in this case? Thankyou.
Sure you can:
Dictionary<String, Delegate> m_events = new Dictionary<String, Delegate>();
In Jeff Richter's Book CLR via C# you can find a complete implementation of an EventSet class.
(Even if you don't have the book you can download the sample code from https://www.wintellect.com/wp-content/uploads/2017/09/CLR-via-C-4th-Edition-Code.zip. The class in question is in "Ch11-1-EventSet.cs".)
As far as the question goes what an event is:
Say you have this line of code in your class:
public event EventHandler<NewMailEventArgs> NewMail;
then the C# compiler will translate the line above into three constructs:
A private delegate field. (Something like: private EventHandler<NewMailEventArgs> NewMail;
A public add_Xxx method (where Xxx is the Event name) that interested subscribers can use to register a callback delegate with the event.
A public remove_Xxx method (where Xxx is the Event name) that subscribers can use to unregister from the event.
(All the gory details can be found in section 11.2 of aforementioned book.)
So in other words the 'thing' you can store in your Event collection class is just a plain old delegate nothing else.
An event is like a property or method; you cant store instances of them. You can use reflection, but this likely isn't what you want.
Did you mean you want to store a list of methods to call when your event is fired? Events already do that.
EDIT:
Ah, I think I get it now. If you just simply fire your event, only the B instances that want it will get it. If you pass into the event args the instance of A that fired the event, then the B instance will be able to tell where it came from. But a B will never get an event from an A that it didn't ask for an event.
EDIT:
Maybe not. You only want to fire an event on one of the B's? Make B implement an interface that has a callback method. Instead of firing an event, call the method on the correct instance of B.
You could use IList with out any trouble. Type T could be any type defined in .net or user defined class.
I read a question ages ago "How do C# Events work behind the scenes?" and Jon answered that all events are similar to methods...
In a purely hypothetical situation, I was wondering if someone could explain or point me to a resource that says when to use an event over a method?
Basically, If I want to have a big red/green status picture which is linked to a Bool field, and I wanted to change it based on the value of the bool, should I:
a) Have a method called Changepicture which is linked to the field and changes the state of the bool and the picture.
b) Have a get/set part to the field and stick an event in the set part.
c) Have a get/set part to the field and stick a method in the set part.
d) Other?
To gain more information about events see this post.
You have options.
If your object already implements INotifyPropertyChanged and your red/green picture is a control which supports databinding, then you can simply fire the NotifyPropertyCHanged event on the bool's set method, and add a databinding on that property to your control.
If not implementing INotifyPropertyChanged, I would still recommend doing something similar. I.e. creating your own event handler, and having the reg/green picture subscribe to the event. Just straight up calling a method from the set of your property creates a tight coupling, which is generally a bad thing to do.
The answer is: It depends.
If your boolean value is in the codebehind class of your visual component (e.g. WinForm) you can call a method ChangePicture without doing strange things. But if your boolean value is architectural more far away from the visual component an event is the right way to handle the scenario because you can not easily call a method on the visual component because the class that contains the boolean value perhaps doesn´t even know your visual component exists. :)
The best way to figure out what you should do is to look at classes in the .NET framework and see how they are designed.
Methods are "doers" or "actions", while you can see events as notification mechanisms. That is if others could be interested is being notified when something happens in an object then you can surface an event and have one or more subscribers to these events.
Since events in .NET are multi-cast, meaning multiple objects can subscribe and therefore be notified of an event happening, that may be other reason to raise an event in your objects. Events also follow the observer pattern in that the subject (your class) is really unaware of the subscribers (loosely coupled). While in order to call a method, the secondary object needs to have a reference to an instance of your class.
Note that, a method in your class eventually raises and event. So let's say you have a method in your class called ChangePicture. Then in the method's implementation, you could eventually raise an event PictureChanged. if someone is interested in being notified of this event, they can subscribe to this event. This someone is typically not the one that made the method call to change the picture.
Events are delegates. Delegates are objects. Event's are actually MulticastDelegates (a base class in the .NET framework). These objects eventually call a method, which is the method that gets called as part of the event notification. So they are slightly "heavier" then just a method call, but that should almost never determine your design.
I have a class that I want to be able the handle the mouse up event for a grid.
I tried to create it with a static method call like this:
MyDataBinding.BindObjectsToDataGrid(ListOfObjectsToBind, myGrid.MouseUp);
The end goal being that in the method I would assign a delegate to the MouseUp
PassedInMouseUp += myMethodThatWillHandleTheMouseUp;
Looks good here (to me) but the compiler chokes on the first line. It says that I can only use MouseUp with a += or a -=.
Clearly I am going about this the wrong way. How can I get a different class to handle the mouse up with out having to:
Pass in the whole grid
Expose the method that will be handling the mouse up as a public method.
Or, is this just a limitation and I will have to do one of the above?
This is not possible without reflection.
Like properties, .Net events compile to a pair of accessor methods - add_EventName and remove_EventName. There is nothing that you can pass as an argument.
Is there a way to tell if an event handler has been added to an object? I'm serializing a list of objects into/out of session state so we can use SQL based session state... When an object in the list has a property changed it needs to be flagged, which the event handler took care of properly before. However now when the objects are deserialized it isn't getting the event handler.
In an fit of mild annoyance, I just added the event handler to the Get property that accesses the object. It's getting called now which is great, except that it's getting called like 5 times so I think the handler just keeps getting added every time the object is accessed.
It's really safe enough to just ignore, but I'd rather make it that much cleaner by checking to see if the handler has already been added so I only do so once.
Is that possible?
EDIT: I don't necessarily have full control of what event handlers are added, so just checking for null isn't good enough.
I recently came to a similar situation where I needed to register a handler for an event only once. I found that you can safely unregister first, and then register again, even if the handler is not registered at all:
myClass.MyEvent -= MyHandler;
myClass.MyEvent += MyHandler;
Note that doing this every time you register your handler will ensure that your handler is registered only once.
Sounds like a pretty good practice to me :)
From outside the defining class, as #Telos mentions, you can only use EventHandler on the left-hand side of a += or a -=. So, if you have the ability to modify the defining class, you could provide a method to perform the check by checking if the event handler is null - if so, then no event handler has been added. If not, then maybe and you can loop through the values in
Delegate.GetInvocationList. If one is equal to the delegate that you want to add as event handler, then you know it's there.
public bool IsEventHandlerRegistered(Delegate prospectiveHandler)
{
if ( this.EventHandler != null )
{
foreach ( Delegate existingHandler in this.EventHandler.GetInvocationList() )
{
if ( existingHandler == prospectiveHandler )
{
return true;
}
}
}
return false;
}
And this could easily be modified to become "add the handler if it's not there". If you don't have access to the innards of the class that's exposing the event, you may need to explore -= and +=, as suggested by #Lou Franco.
However, you may be better off reexamining the way you're commissioning and decommissioning these objects, to see if you can't find a way to track this information yourself.
If this is the only handler, you can check to see if the event is null, if it isn't, the handler has been added.
I think you can safely call -= on the event with your handler even if it's not added (if not, you could catch it) -- to make sure it isn't in there before adding.
This example shows how to use the method GetInvocationList() to retrieve delegates to all the handlers that have been added. If you are looking to see if a specific handler (function) has been added then you can use array.
public class MyClass
{
event Action MyEvent;
}
...
MyClass myClass = new MyClass();
myClass.MyEvent += SomeFunction;
...
Action[] handlers = myClass.MyEvent.GetInvocationList(); //this will be an array of 1 in this example
Console.WriteLine(handlers[0].Method.Name);//prints the name of the method
You can examine various properties on the Method property of the delegate to see if a specific function has been added.
If you are looking to see if there is just one attached, you can just test for null.
The only way that worked for me is creating a Boolean variable that I set to true when I add the event. Then I ask: If the variable is false, I add the event.
bool alreadyAdded = false;
This variable can be global.
if(!alreadyAdded)
{
myClass.MyEvent += MyHandler;
alreadyAdded = true;
}
If I understand your problem correctly you may have bigger issues. You said that other objects may subscribe to these events. When the object is serialized and deserialized the other objects (the ones that you don't have control of) will lose their event handlers.
If you're not worried about that then keeping a reference to your event handler should be good enough. If you are worried about the side-effects of other objects losing their event handlers, then you may want to rethink your caching strategy.
i agree with alf's answer,but little modification to it is,,
to use,
try
{
control_name.Click -= event_Click;
main_browser.Document.Click += Document_Click;
}
catch(Exception exce)
{
main_browser.Document.Click += Document_Click;
}
EventHandler.GetInvocationList().Length > 0