So far in my limited use of c# I've subscribed to a couple of events.
Why do the following two code snippets use two diferent words EventHandler and ElapsedEventHandler?
myProcess = new Process();
myProcess.StartInfo.FileName = #"notepad.exe";
myProcess.Exited += new EventHandler(MyProcessExited);
compared to:
myTimer = new System.Timers.Timer(tickLength);
myTimer.Elapsed += new ElapsedEventHandler(myTimer_Elapsed);
are these words EventHandler and ElapsedEventHandler specific to the object whose event I'm using? Or are there a whole set of objects where I have to use "EventHandler" and different set of objects where I have to use "ElapsedEventHandler" ? (i.e for set X of objects we use xxxxxEventHandler etc)
Are these words EventHandler and ElapsedEventHandler specific to the
object whose event I'm using?
This words are names of delegates. And they are specific to information, which is passed from object, which raised event to subscribers of that event. Read about events, delegates and parameters passing below.
An event in C# is a way for a class to provide notifications to clients of that class when some interesting thing happens to an object. Events are declared using delegates.
So, if you want to notify client of some interesting thing happened in your object, you can declare delegate:
public delegate void SomethingHappenedDelegate();
And declare event of this type in your class:
class Foo
{
public event SomethingHappenedDelegate SomethingHappened;
// other code, which raises SomethingHappened event
// when something interesting happened
}
When clients of this class want to be notified that something interesting happenedthey should subscribe to SomethingHappened event. And event handler signature must match type of event (SomethingHappenedDelegate in this case):
static void Main(string[] args)
{
Foo foo = new Foo();
foo.SomethingHappened += new SomethingHappenedDelegate(Foo_SomethingHappened);
}
// this method should be: void MethodName()
static void Foo_SomethingHappened()
{
// you notified, that something happened
}
When you need to pass some additional data when notifying clients, you should use different delegate type, which takes some arguments. For example, if you want to pass some message to clients, change event's delegate type:
public delegate void SomethingHappenedDelegate(string message);
Now you should change event handler to correspond this delegate type:
static void Foo_SomethingHappened(string message)
{
// you notified, that something happened and you recieved a message
}
Another example, if we want to pass instance of object, which raised an event, and some additional arguments message and time:
public delegate void SomethingHappenedDelegate(object sender, string message, DateTime time);
Then we should have event handler with same signature:
static void Main(string[] args)
{
Foo foo1 = new Foo();
foo1.SomethingHappened += new SomethingHappenedDelegate(Foo_SomethingHappened);
Foo foo2 = new Foo();
foo2.SomethingHappened += new SomethingHappenedDelegate(Foo_SomethingHappened);
}
// we use same event handler for all SomethingHappened events
static void Foo_SomethingHappened(object sender, string message, DateTime time)
{
Foo foo = sender as Foo; // now we get object, which raised event
// and we can use message and time
}
So, all events have type of some delegate. Definition of that delegate describes which parameters should receive method, which will be handling event. In your code you use two events - Process.Exited and Timer.Elapsed. Lets look how these events defined:
public event EventHandler Exited;
public event ElapsedEventHandler Elapsed;
You can see here different types of delegates:
public delegate void EventHandler(object sender, EventArgs e);
public delegate void ElapsedEventHandler(object sender, ElapsedEventArgs e);
As you can see from definition, these delegates have same first parameter (sender), and different last parameter. So, handlers of these events also should have different signature, which should match these delegates.
You may wonder, why name of second parameter ends with EventArgs. If you want to notify other objects, that SomethingHappened, then following convention exists in C#:
event name should be SomethingHappened
if clients need some additional data to be passed, these data encapsulated in object, which derives from EventArgs class: SomethingHappenedEventArgs.
event delegate should be EventHandler (if no additional info should be passed to clients), or it should be of generic type EventHandler<SomethingHappenedEventArgs>, or it should be custom delegate with name SomethingHappenedEventHandler and signature void (object sender, SomethingHappenedEventArgs e)
Read more here.
No, the event names are matter of framework designer choice + they are toughly coupled to delegates that rapresent the event of any specific object.
EventHandler defininton is:
[Serializable]
[ComVisible(true)]
public delegate void EventHandler(object sender, EventArgs e);
ElapsedEventHandler definiton is:
public delegate void ElapsedEventHandler(object sender, ElapsedEventArgs e);
As you can see they are different delegate types.
Related
So I have an event declared as follows:
public event EventHandler OnChangeDetected;
Then I have the following handler that gets assigned to that event.
myObject.OnChangeDetected += OnTableChanged;
My understanding is that this type of event would require that my OnTableChanged method to have the following signature which compiles just fine.
public void OnTableChanged(object sender, EventArgs e)
Now I want to replace the OnTableChanged event to the following signature.
public void OnTableChanged(SqlChangeNotifier sender, SqlNotificationEventArgs e)
However, when I replace the parameters with derived types, it complains that there is no overload for "OnTableChanged" that matches delegate EventHandler. Since SqlChangeNotifier derives from Object, and SqlNotificationEventArgs derives from EventArgs, can anyone explain why I cannot have these derived parameters types since they inherit from the correct base types?
EventHandler is a delegate of the type void EventHandler(object sender, EventArgs e). So the signature of the handlers that subscribe to the event has to match this.
Now void OnTableChanged(SqlChangeNotifier sender, SqlNotificationEventArgs e) is more specific than that: It can no longer take any sender object and the event arguments also have to be of type SqlNotificationEventArgs.
The problem is now that when an event is raised, the original sender of the event will try to call the event handlers with the arguments object sender, EventArgs e but your method requires more specialized types. There is no guarantee from the type system that these arguments are in fact of those specialized types.
If you want to require these types, you will need to change the type of your event to a more restrictive delegate type.
You can't do that because the subscriber of the event should get a derived instance, but the publisher can only provide a base instance.
You are allowed to do the opposite though:
public static event KeyEventHandler ChangeDetected; // handler with derived args signature
private static void Program_ChangeDetected(object sender, EventArgs e) // base event args are OK
{
Console.WriteLine("ChangeDetected");
}
static void Main(string[] args)
{
ChangeDetected += Program_ChangeDetected;
ChangeDetected?.Invoke(null, new KeyEventArgs(default)); // base event args are NOT OK
}
For the overall topic on this sort of change, see Contravariance and Covariance.
Keep in mind that event systems are just fancy ways of calling a series of method which you can swap out at runtime, so if you can't call the handler directly with the exact same arguments you pass to raise the event, then the event system can't do it either.
Like methods, event handlers are Contravariant, meaning that a delagate of type EventHandler<SpecializedEventArgs> (assuming SpecializedEventArgs : EventArgs) will accept a handler of signature public void Handler(object sender, EventArgs args) because the call to the event ends up calling the handler with a SpecializedEventArgs object, which can be implicitly converted to EventArgs by simple polymorphism. IE the following will compile:
public static event EventHandler<SpecializedEventArgs> Event;
public static void Handler(object sender, EventArgs args) { }
public static void Main() {
Event += Handler;
//...
I want to make a list that stores some events and attach event handler to the event through the list.
So I make a List<dele> add the event anEvent into it, then I try to attach an event handler to that event, but at last anEvent still got no event handler attached to it, the program outputs True. But the delegate stored at list[0] did get the lambda expression.
public delegate void dele();
class Program
{
static event dele anEvent;
static void Main(string[] args)
{
List<dele> list=new List<dele>();
list.Add(anEvent);
list[0]+=()=>{Console.WriteLine("BEEP!");};
Console.WriteLine(anEvent==null);
}
}
Isn't delegate a reference type? It seems that eventhandler and list[0] refers to different objects. I wonder why.
If I want that anEvent gets the event handler when I attach the handler to list[0], what should I do?
Thanks!
A delegate is basically a contract for a method implementation.
Kinda like an interface is a contract for class implementations.
The CLI (Common Language Infrastructure) specification says that delegates are reference types.
A delegate is a reference type that can be used to encapsulate a named
or an anonymous method. Delegates are similar to function pointers in
C++; however, delegates are type-safe and secure. For applications of
delegates, see Delegates and Generic Delegates.
Have a look at this question and also have a look at that one.
I have converted the method into a non anonymous method for debugging reasons
public delegate void dele();
public static event dele anEvent;
static void Main(string[] args) {
List<dele> list = new List<dele>();
list.Add(anEvent);
list[0] += Dele;
list[0].Invoke(); //this actually gets invoked and does not throw!
anEvent = list[0];
Console.WriteLine(anEvent == null); //the output is false
anEvent.Invoke(); // this also gets s invoked and does not throw
}
private static void Dele() { //this gets invoked 2 times as expected
Console.WriteLine("Beep"); // this gets printed after invoking the event
}
I have some code where I need to dynamically bind events to an event handler:
foreach (string evnt in attribute.Events)
{
EventInfo ei = control.GetType().GetEvent(evnt);
if(ei != null)
{
ei.AddEventHandler(control, new EventHandler((s, e) =>
{
// More awesomeness here...
}));
}
}
So for each string in the event list, get the event from the control and bind a handler.
The problem is that not all events are EventHandler, some for example might be KeyEventHander or MouseEventHandler etc.
I don't want a massive if/else list of EventHandler derived types, I just want to bind the same handler regardless of what type of EventHandler it is.
How can I do this?
Here is one way to do it:
First create this helper class:
public class HandlerHelper<T> where T : EventArgs
{
private readonly EventHandler m_HandlerToCall;
public HandlerHelper(EventHandler handler_to_call)
{
m_HandlerToCall = handler_to_call;
}
public void Handle(object sender, T args)
{
m_HandlerToCall.Invoke(sender, args);
}
}
This generic class has a Handle method that will be used to be our delegate for the many event handler types. Note that this class is generic. T will be one of the many EventArgs derived classes for different event types.
Now let's say you define the following event handler:
var event_handler = new EventHandler((s, args) =>
{
// More awesomeness here...
});
Here is how you can use the helper class to create different delegates for the different event handler types that will invoke event_handler:
foreach (var event_name in event_names)
{
var event_info = control.GetType().GetEvent(event_name);
var event_handler_type = event_info.EventHandlerType;
var event_args_type = event_handler_type.GetMethod("Invoke").GetParameters()[1].ParameterType;
var helper_type = typeof(HandlerHelper<>).MakeGenericType(event_args_type);
var helper = Activator.CreateInstance(helper_type, event_handler);
Delegate my_delegate = Delegate.CreateDelegate(event_handler_type, helper, "Handle");
event_info.AddEventHandler(button, my_delegate);
}
For each event, we obtain the EventHandlerType which is like EventHandler or MouseEventHandler.
Then we use reflection to get the type of the second parameter which is like EventArgs or MouseEventArgs.
Then we create an instance of HandlerHelper<> based on the type of the EventArgs parameter. For example HandlerHelper<EventArgs> or HandlerHelper<MouseEventArgs>.
We give event_handler to the constructor of HandlerHelper so that it can call it when it's Handle method is invoked.
This makes the signature of the Handle method as we want. For example, in the case of HandlerHelper<MouseEventArgs>, the signature of the Handle method is:
void Handle(object sender, MouseEventArgs args)
Now, we use Delegate.CreateDelegate to create a delegate based on the Handle method for the specific HandlerHelper object that we created, and we give such delegate to the AddEventHandler method.
For performance reasons, you can cache the delegate (instead of creating one every time) based on event_args_type.
You can use dynamic key word to attach the handler. More explanation you can find here: https://msdn.microsoft.com/en-us/library/ms228976(v=vs.110).aspx
When I try to call the event myevent in the function fire, it gives compiler error as
delegate mydelegate does not take 0 arguments.
if i try to give arguments in calling myevent as
myevent(this);
it again shows error as it does not take 1 parameters. what parameters am i supposed to give in the calling myevent? Here's the program code:
namespace useofdelegates
{
public class Class1
{ int i;
public delegate void mydelegate(object sender,EventArgs e);
public event mydelegate myevent;
public void fire()
{
myevent(this); // *** shows compiler error ***
}
}
}
The compiler error is because you are using a delegate with two parameters and calling it with one.
The simple solution is
myevent(this, EventArgs.Empty);
The more regular (and thread-safe) solution, which I think you're getting confused with is
OnMyEvent(EventArgs.Empty);
// and then later
public void OnMyEvent(EventArgs args)
{
mydelegate handler = myevent;
if(handler != null) handler(this, args);
}
The reason for doing it this way is that you should always make sure that there is at least one handler attached to the event. There is a small chance that the handler can be detached between checking for null and calling the handler, so it's good to pass it on to another variable which it cannot be detached from before checking for null.
You need to pass in 2 parameters. the first one being the source of the event. In your example "this" would work. The second one being of type EventArgs or a realted subclass. You can also pass EventArgs.Empty is you do not wish to pass any event especific information to the event handler.
So your method would look like this
fire(this, EventArgs.Empty);
or
fire(this, new MyCustomEVentArgs());
Here is an MSDN link with an example of using events http://msdn.microsoft.com/en-us/library/aa645739(VS.71).aspx
and nother one on creating custom EventArg subclasses to use in your handler. http://www.devarticles.com/c/a/C-Sharp/Creating-Custom-Delegates-and-Events-in-C-sharp/
You have declared mydelegate to require an object and an EventArgs, so you have to pass these in. Of course, you don't have declare your delegate with these arguments. You could just declare it thus:
public delegate void mydelegate();
or just pass this as the sender:
public delegate void mydelegate(object sender);
Just create an empty EventArgs I guess.
myevent(this, new EventArgs());
Note, here is the full code:
namespace useofdelegates
{
public class Class1
{
int i;
public delegate void mydelegate(object sender, EventArgs e);
public event mydelegate myevent;
public void fire()
{
myevent(this, new EventArgs()); // *** shows compiler error ***
}
}
}
I want to be able to find out if an event is hooked up or not. I've looked around, but I've only found solutions that involved modifying the internals of the object that contains the event. I don't want to do this.
Here is some test code that I thought would work:
// Create a new event handler that takes in the function I want to execute when the event fires
EventHandler myEventHandler = new EventHandler(myObject_SomeEvent);
// Get "p1" number events that got hooked up to myEventHandler
int p1 = myEventHandler.GetInvocationList().Length;
// Now actually hook an event up
myObject.SomeEvent += m_myEventHandler;
// Re check "p2" number of events hooked up to myEventHandler
int p2 = myEventHandler.GetInvocationList().Length;
Unfort the above is dead wrong. I thought that somehow the "invocationList" in myEventHandler would automatically get updated when I hooked an event to it. But no, this is not the case. The length of this always comes back as one.
Is there anyway to determine this from outside the object that contains the event?
If the object concerned has specified the event keyword, then the only things you can do are add (+=) and remove (-=) handlers, nothing more.
I believe that comparing the invocation list length would work, but you need to be operating inside the object to get at it.
Also, keep in mind that the += and -= operators return a new event object; they don't modify an existing one.
Why do you want to know if a particular event is hooked up? Is it to avoid registering multiple times?
If so, the trick is to remove the handler first (-=) as removing a handler that's not there is legal, and does nothing. Eg:
// Ensure we don't end up being triggered multiple times by the event
myObject.KeyEvent -= KeyEventHandler;
myObject.KeyEvent += KeyEventHandler;
There is a subtle illusion presented by the C# event keyword and that is that an event has an invocation list.
If you declare the event using the C# event keyword, the compiler will generate a private delegate in your class, and manage it for you. Whenever you subscribe to the event, the compiler-generated add method is invoked, which appends the event handler to the delegate's invocation list. There is no explicit invocation list for the event.
Thus, the only way to get at the delegate's invocation list is to preferably:
Use reflection to access the compiler-generated delegate OR
Create a non-private delegate (perhaps internal) and implement the event's add/remove methods manually (this prevents the compiler from generating the event's default implementation)
Here is an example demonstrating the latter technique.
class MyType
{
internal EventHandler<int> _delegate;
public event EventHandler<int> MyEvent;
{
add { _delegate += value; }
remove { _delegate -= value; }
}
}
It can be done, but it takes some hackery... as mentioned above the compiler generates the implementation of the event, including its backing field. Reflection lets you retrieve the backing field by name, and once you have access to it you can call GetInvocationList() even though you're outside the class itself.
Since you're asking to use reflection to get the event by name I assume you're also using reflection to get the Type by name--I'm whipping up an example that will show how to do it.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Reflection;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string typeName = "ConsoleApplication1.SomeClass, ConsoleApplication1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null";
string eventName = "SomeEvent";
Type declaringType = Type.GetType(typeName);
object target = Activator.CreateInstance(declaringType);
EventHandler eventDelegate;
eventDelegate = GetEventHandler(target, eventName);
if (eventDelegate == null) { Console.WriteLine("No listeners"); }
// attach a listener
SomeClass bleh = (SomeClass)target;
bleh.SomeEvent += delegate { };
//
eventDelegate = GetEventHandler(target, eventName);
if (eventDelegate == null)
{
Console.WriteLine("No listeners");
}
else
{
Console.WriteLine("Listeners: " + eventDelegate.GetInvocationList().Length);
}
Console.ReadKey();
}
static EventHandler GetEventHandler(object classInstance, string eventName)
{
Type classType = classInstance.GetType();
FieldInfo eventField = classType.GetField(eventName, BindingFlags.GetField
| BindingFlags.NonPublic
| BindingFlags.Instance);
EventHandler eventDelegate = (EventHandler)eventField.GetValue(classInstance);
// eventDelegate will be null if no listeners are attached to the event
if (eventDelegate == null)
{
return null;
}
return eventDelegate;
}
}
class SomeClass
{
public event EventHandler SomeEvent;
}
}
You should be able to get the invocation list via the "event". Roughly, it will be something like..
public delegate void MyHandler;
public event MyHandler _MyEvent
public int GetInvocationListLength()
{
var d = this._MyEvent.GetInvocationList(); //Delegate[]
return d.Length;
}
I used your example and modified it a little bit. registering an event handler increases the number of invocations. even when using two different callback methods (as shown here) or using the same callback method.
private void SomeMethod()
{
// Create a new event handler that takes in the function I want to execute when the event fires
var myEventHandler = new EventHandler(OnPropertyChanged);
// Get "p1" number events that got hooked up to myEventHandler
int p1 = myEventHandler.GetInvocationList().Length; // 1
// Now actually hook an event up
myEventHandler += OnPropertyChanged2;
// Re check "p2" number of events hooked up to myEventHandler
int p2 = myEventHandler.GetInvocationList().Length; // 2
myEventHandler.Invoke(null, null);
// each of the registered callback methods are executed once.
// or if the same callback is used, then twice.
}
private void OnPropertyChanged2(object? sender, EventArgs e)
{}
private void OnPropertyChanged(object? sender, EventArgs e)
{}
As others already mentioned, the access to eventhandler.GetInvocationList is limited to the class itself, you need to expose a property or method to retrieve the delegate list.
Like this:
protected Delegate[]? GetInvocations() => PropertyChanged?.GetInvocationList();
depending on your usage make it protected, internal or both.