The following is an example from MSN website. This is a good example, I just do not understand what this line:
EventHandler<CustomEventArgs> handler = RaiseCustomEvent;
is doing?
Is not RaiseCustomEvent an event based on the definition at the top of the program?
Why is an Event is equated to an EventHandler? These are two different types.
Where is RaiseCustomEvent being initialized? If it is not initialized how can we copy it or why do we want to copy something uninitialized to something else?
What is the handler variable there for? Is that an event or an event handler?
I'm very confused and am trying hard to get this event/event handler/delegate issue understood.
Here is the sample code from MSN
namespace DotNetEvents
{
using System;
using System.Collections.Generic;
// Define a class to hold custom event info
public class CustomEventArgs : EventArgs
{
public CustomEventArgs(string s)
{
message = s;
}
private string message;
public string Message
{
get { return message; }
set { message = value; }
}
}
// Class that publishes an event
class Publisher
{
// Declare the event using EventHandler<T>
public event EventHandler<CustomEventArgs> RaiseCustomEvent;
public void DoSomething()
{
// Write some code that does something useful here
// then raise the event. You can also raise an event
// before you execute a block of code.
OnRaiseCustomEvent(new CustomEventArgs("Did something"));
}
// Wrap event invocations inside a protected virtual method
// to allow derived classes to override the event invocation behavior
protected virtual void OnRaiseCustomEvent(CustomEventArgs e)
{
// Make a temporary copy of the event to avoid possibility of
// a race condition if the last subscriber unsubscribes
// immediately after the null check and before the event is raised.
EventHandler<CustomEventArgs> handler = RaiseCustomEvent;
// Event will be null if there are no subscribers
if (handler != null)
{
// Format the string to send inside the CustomEventArgs parameter
e.Message += String.Format(" at {0}", DateTime.Now.ToString());
// Use the () operator to raise the event.
handler(this, e);
}
}
}
//Class that subscribes to an event
class Subscriber
{
private string id;
public Subscriber(string ID, Publisher pub)
{
id = ID;
// Subscribe to the event using C# 2.0 syntax
pub.RaiseCustomEvent += HandleCustomEvent;
}
// Define what actions to take when the event is raised.
void HandleCustomEvent(object sender, CustomEventArgs e)
{
Console.WriteLine(id + " received this message: {0}", e.Message);
}
}
class Program
{
static void Main(string[] args)
{
Publisher pub = new Publisher();
Subscriber sub1 = new Subscriber("sub1", pub);
Subscriber sub2 = new Subscriber("sub2", pub);
// Call the method that raises the event.
pub.DoSomething();
// Keep the console window open
Console.WriteLine("Press Enter to close this window.");
Console.ReadLine();
}
}
}
An event is to a delegate type what a property is to any other type.
If you have a property such as this:
public string Name {get;set;}
Obviously you can do something like this:
string name = Name;
The property has an underlying string value that is accessed/modified by the property.
Similarly, an event has a delegate under the hood, and that delegate is of the type defined in the event declaration. That it's an event simply defines how handlers of that event are added/removed from that underlying delegate.
From within the declaring type (that's a key point; you can't do this externally) you can access that underlying delegate in order to invoke it. That's the reason for doing the code that you see; they're accessing the underlying delegate so that they can verify that it has some handlers within it, and if so, it invokes them.
So, to answer the questions explicitly:
Is not RaiseCustomEvent an event based on the definition at the top of the program?
RaiseCustomEvent is the type of the underlying delegate that the event wraps.
Why is an Event is equated to an EventHandler? These are two different types.
It's not strictly equality. It's pulling out the underlying delegate from within the event.
Where is RaiseCustomEvent being initialized? If it is not initialized how can we copy it or why do we want to copy something uninitialized to something else?
In this case it's using the automatic add/remove implementations that the framework will provide, rather than manually defining them. The automatically defined add handler will initialize the underlying delegate if it is currently null. If the event declaration would
define a custom add handler, it would need to handle that case.
What is the handler variable there for? Is that an event or an event handler?
It is a single delegate that represents the combination of all of the event handlers. Within it's definition will be an invocation list of all of the individual methods that make up that delegate. So it's not a single event handler, it's the collection of all of them. Since it's pulled out of the event it no longer strictly represents that event; it's a copy of what was in the event at some point in the past. You cannot change the event (i.e. add a new handler) using the delegate that you've pulled out of it.
I'll try the four questions you pose:
1) Is not RaiseCustomEvent an event based on the definition at the top of the program ?
The CustomEventArgs class holds some data (arguments) for the event we want to declare. It is used as type parameter in the type EventHandler<CustomEventArgs>. This last type is a delegate type, which means it represents one or more methods of the same signature and return type. (Zero methods will be a null reference as the value of the delegate.)
The type of the event RaiseCustomEvent is that delegate type, EventHandler<CustomEventArgs>.
2) Why an Event is equated to an EventHandler? These are two diffeent types
An event consists on a pair of special methods, accessors, one add accessor and one remove accessor. Both have one parameter of the same type which is called the type of the event. That type must be a delegate type. In this case that type is EventHandler<CustomEventArgs>.
In this example the event is a so-called field-like event. It generates a backing field of the same type, EventHandler<CustomEventArgs>, the delegate type. That field has the same name as the event itself!
When they do:
// Make a temporary copy of the event to avoid possibility of
// a race condition if the last subscriber unsubscribes
// immediately after the null check and before the event is raised.
EventHandler<CustomEventArgs> handler = RaiseCustomEvent;
they copy the current value of that backing field into the local variable handler. The comment describes why. They want to check for null before they invoke the delegate.
3) Where is RaiseCustomEvent have been initialized? If not initialized how can we copy or why do we want to copy something uninitialized to soemthig else.
It usually gets initialized when people use the add accessor of the event. This takes place through the special C# syntax +=. This is called subscribing to the event. See the Subscriber class.
Actually pub.RaiseCustomEvent += HandleCustomEvent; translates to pub.add_RaiseCustomEvent(HandleCustomEvent);, so it is a call to the add accessor. The add accessor is generated by the compiler (in a field-like event), and it initializes the backing field.
4)I do not know what is handler variable? is that an event or an event handler ?
It is a delegate. It is not an event. It is a copy of the backing field of the field-like event at one moment in time.
Related
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
}
Is there anything wrong with subscribing to event with
MyPopup.CustomPopupPlacementCallback = popupFixCentered;
instead of:
MyPopup.CustomPopupPlacementCallback += popupFixCentered;
For example, if I am changing to different callback from another method I want to make sure I have only one callback subscribed without needing to -= the correct one.
Well, it sounds like you didn't try it. If you did, you would get the following compilation error:
The event 'XXX' can only appear on the left hand side of += or -= (except when used from within the type 'YourClass')
The error is pretty clear: you can only use the += and -= operators on the event.
If you try to assign to the event from within the class that defines the event, then it will "work". But the reason it appears to be able to assign to an event in that case is because it's actually not accessing the event. It's accessing an auto-generated private delegate instance that you may not realize is actually there.
Quoting from Chris Burrows' article on the subject:
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.
To understand this, you need to visualize that when you define an event such as:
public event EventHandler MyEvent;
... what you don't see, is that it actually gets translated into something like this (I'm copying this from Jon Skeet's article on events and delegates. Also note that the exact code it gets translated into has changed between versions of C#, so it may be a bit different, but the general idea is the same):
private EventHandler _myEvent;
public event EventHandler MyEvent
{
add
{
lock (this)
{
_myEvent += value;
}
}
remove
{
lock (this)
{
_myEvent -= value;
}
}
}
So when you access MyEvent from outside the class, you can only invoke the add and remove methods through the += and -= operators.
But from within the class, accessing MyEvent means something different. It actually becomes a reference to that private _myEvent delegate variable that you can't see, but it is there. Because this is a delegate type, then you can use the assignment (=) operator on it.
So, to achieve what you want, you could define a public method in the same class that defines the event, and use that method to set your new event handler.
Something like this:
public class MyClass
{
public event EventHandler MyEvent;
public void setSingleEventHandler(EventHandler eventHandler)
{
this.MyEvent = eventHandler;
}
}
But if you are going to do that, then it defeats the purpose of the event type. If you only want to invoke a single event handler at most at any given time, then defining it this way (without using the event keyword) makes more sense:
public class MyClass
{
public EventHandler MyEvent { get; set; }
}
References
Jon Skeet article: Delegates and Events
Chris Burrows article: (also check out the rest of the series): Events get a little overhaul in C# 4, Part II: Semantic Changes and +=/-=
I just tested it. Yes, you can use the = operator to assign to an event. (Edit: Apparently only from within the same class)
delegate void Foo();
event Foo bar;
Method()
{
bar = () => { Console.WriteLine("1"); };
bar();
bar = () => { Console.WriteLine("2"); };
bar();
}
Produces the output:
1
2
But if you try to assign from outside the class, it will give you an error.
You can get around this by using a java-style set method:
SetBar(Foo foo)
{
bar = foo;
}
Only time I'd ever recommend java convention for external access of properties :)
Is there some hidden class property which would allow to know this ?
If you have access to the actual delegate (if you're using the shorthand event syntax, then this is only within the actual declaring class, as the delegate is private), then you can call GetInvocationList().
For instance:
public event EventHandler MyEvent;
To get the list of subscribers, you can call:
Delegate[] subscribers = MyEvent.GetInvocationList();
You can then inspect the Method and Target properties of each element of the subscribers array, if necessary.
The reason this works is because declaring the event as we did above actually does something akin to this:
private EventHandler myEventDelegate;
public event EventHandler MyEvent
{
add { myEventDelegate += value; }
remove { myEventDelegate -= value; }
}
This is why the event looks different when viewed from within the declaring class compared to anywhere else (including classes that inherit from it). The only public-facing interface is the add and remove functionality; the actual delegate, which is what holds the subscriptions, is private.
I have a basic doubt. Internally how are events represented as methods or as (fields)objects. If event is a field then how one can still contain events in the interface definition.
Thanks
JeeZ
If you type this:
public event EventHandler MyEvent;
what the compiler generates is (simplified) this:
// declares a normal delegate of type 'EventHandler'
private EventHandler _myEvent;
// declares 'add_MyEvent' and 'remove_MyEvent' methods similar to a property
public event EventHandler MyEvent {
add { _myEvent += value; }
remove { _myEvent -= value; }
}
An event is similar to a property; a wrapper around a delegate that only allows methods to be added or removed. This is so you can't completely re-assign the delegate and delete other people's subscriptions to it.
All you are doing when specifying an event in an interface is that any implementing classes should have the add and remove methods for the event. Very similar to declaring a property on an interface, in fact.
This is also why you can only call or re-assign the event in the class it is declared in - any references to the MyEvent event within the class are re-routed to use the delegate directly, whereas outside the class you can only access the add and remove methods, not the delegate directly.
#thecoop's answer is a very good description of "field-like events" (noting the "simplified" caveat) - but note that actually events can be implemented any way you like. All the event defines is an add/remove accessor pair (which is why it can be defined on the interface, like a property).
For example, with sparce events the following may be common:
private static readonly object FooKey = new object(), BarKey = new object();
public event EventHandler Foo {
add {Events.AddHandler(FooKey, value);}
remove {Events.RemoveHandler(FooKey, value);}
}
public event MouseClickEventHandler Bar {
add {Events.AddHandler(BarKey, value);}
remove {Events.RemoveHandler(BarKey, value);}
}
where Events is an EventHandlerList, usually delay-loaded:
private EventHandlerList events;
protected EventHandlerList Events {
get {
if(events == null) events = new EventHandlerList();
return events;
}
}
Or you could do anything else you like. Within reason (noting the expected behaviour of event subscriptions and delegate equality / composite delegates).
Events are not represented as fields nor methods. They are simply events, as far as the meta-data for a class is concerned.
Similarly properties have a special flag (although these get stored as methods with well known names).
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.