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.
Related
Hi I'm new to C# and currently working on an app using some 3rd party APIs. I came across two lines of code which appreciate it if you could help. Below is the code similar to the actual sample code I have
bank.Account.NewAcc += delegate (Customer customerInfo) {
Console.WriteLine ("Customer Name : " + customerInfo.Name);
};
The bank.Account.NewAcc is an event while Customer is a class.
Below are the questions:
Can we create a delegate of object and not method?
How can I invoke it? I don't have any other information provided by the APIs
Thank you very much.
Please find below the answers to your questions as per my knowldege:
1. Can we create a delegate of object and not method?
No. You can't. Because delegates mean Function Pointer. They can hold the reference of functions only. And the method's signature must match with the signature of the delegate.
2. How can I invoke it? I don't have any other information provided from the APIs
The delegate which is associated with an event as an event handler can be invoked in two ways. First, when the event will be raised, second, when you invoke event programmatically.
In your sample code, an anonymous delegate with a single parameter of type CustomerInfo has been bound to an event named NewAcc.
Once the NewAcc event is fired, the bound delegates would be executed, the code to fire \ raise the event could be like this:
public Class BankAccount { .....
protected virtual void OnNewAcc(CustomerInfo customerInfo)
{
CustomerEventHandler handler = NewAcc;
if (handler != null)
{
handler(this, customerInfo);
}
}
public event CustomerEventHandler NewAcc;
.... }
I hope this answered your question.
I am not quite sure what you mean by a delegate of "object". I doubt you can though. Delegates represent a list of methods to be called. You can't just assign any object value to it.
You don't invoke (or raise) events. The API does. Judging from the name of NewAcc, I guess the event will be raised when a new account is created. Try doing that to bank.Account. You might see the customer info getting printed. Obviously, this won't work 100%. You have to look inside bank.Account to know exactly when the event will be raised (or just ask the person who wrote it).
Note that the delegate can be written with a lambda expression:
bank.Account.NewAcc += (customerInfo) => Console.WriteLine ("Customer Name : " + customerInfo.Name);
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.
At one point in my code, i subscribe to the following event :-
UploadFolderMessageQueue.ReceiveCompleted += UploadMSMQReceiveCompleted;
works great and when the Message Queue's Recieved Completed event fires, my delegate handles it.
Now, I'm wanting to CHECK to see if the event has been subscribed to, before I subscribe to it. I get an compile time error when I do :-
// Compile Time Errors...
if (UploadFolderMessageQueue.ReceiveCompleted == null)
{
UploadFolderMessageQueue.ReceiveCompleted += UploadMSMQReceiveCompleted;
UploadFolderMessageQueue.Formatter =
new XmlMessageFormatter(new[] {typeof (string)});
}
The event
'System.Messaging.MessageQueue.ReceiveCompleted'
can only appear on the left hand side
of += or -=
I know this is embarrassingly simple .. but I'm stumped :( Any suggestions?
If you need to make sure that there is only one subscriber you can use the following code:
UploadFolderMessageQueue.ReceiveCompleted -= UploadMSMQReceiveCompleted;
UploadFolderMessageQueue.ReceiveCompleted += UploadMSMQReceiveCompleted;
If UploadFolderMessageQueue.ReceiveCompleted is null then the first line will do nothing, in other case the event handler will be removed. That means UploadFolderMessageQueue.ReceiveCompleted will always have only one subscriber (of course if the UploadMSMQReceiveCompleted is the only one event handler for that event).
you cannot do this from subscriber to the event. Only publisher can check if there are any subscribers. You will need to keep track of subscription using some other mechanism in your class like:
UploadFolderMessageQueue.ReceiveCompleted += UploadMSMQReceiveCompleted;
bool handlerAttached=true;
then you can use this:
if(handlerAttached)
{
//DO YOUR STUFF
}
The null test can only be performed within the class that declares the event (i.e. the UploadFolderMessageQueue type.)
1) If you have access to the source of this class you can add a method or property that does the test and returns a boolean result that you can check before subscribing.
2) If you cannot modify the declaring class, and you are only checking for re-subscriptions from your own code, you can separately record the subscription in a boolean variable, and then check that variable before attempting to (re) subscribe.
3) If you cannot change the declaring class and you are checking for subscriptions from code other than your own, there doesn't appear to be a solution.
Is it possible to instead of doing this:
person.Walking -= person_Walking1;
person.Walking -= person_Walking2;
person.Walking -= person_Walking3;
Do this:
person.Walking = // remove all the handlers without knowing their names
Thanks.
No. Part of the point of events is that they prevent you from doing that. If I've subscribed to a button click and you've subscribed to a button click, what right have you to remove my handler? (Okay, that's anthropomorphising somewhat, but you get the idea.) Note that it's not a matter of knowing the "name" of an event handler - you have to be able to provide a reference to an "equal" delegate instance.
For example, if you subscribe to an event using an anonymous method or a lambda expression, you'll have to keep a reference to that somewhere:
EventHandler handler = (sender, args) => Console.WriteLine("Clicked!");
button.Click += handler;
...
button.Click -= handler;
When you use the name of a method, that's performing a method group conversion from the name of the method to a delegate instance:
button.Click += HandleEvent;
...
button.Click -= HandleEvent;
Here there are two separate delegate instances involved, but they're equal as they have the same invocation list (they do the same thing) and they have the same target (they're doing that thing "on" the same object).
EDIT: I'm assuming you only have access to it as an event, not as a field - if you're writing code in the class which publishes the event, you can do what you like, and setting the field to null (or removing it from the collection, or however your implementation works) is fine.
Sure. Just set:
person.Walking = null;
This is one of the reasons events were invented. If you wanted to do something like that, use delegates.
Not from outside the class in which the events are defined. If you really wanted to, you could define a method like this:
public void ClearEventListeners() {
MyEvent.Clear();
}
(The exact call may be different, if it exists, but IntelliSense should point you in the right direction.)
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