Delegate, why the key word event works with operator ' = '? - c#

I know that the keyword 'event' associated to a delegate variable allow you just to use the operator += and -=, and the operator = is forbidden. I was trying to verify this behaviour, but at the line mydelegate = p.stampaPropUmano;not only Visual Studio doesn't signale me an error, but also all works perfectly. stampaPropUmano and stampaPropAnimale are two methods of the class Umano and Animale respectively.
Do you know the reason? Please let me know if the keyword "event" give other properties.. In my documentation I found just the property that I said before. Regards
namespace csharpWPFevent
{
public delegate void delegato(int index=7);
public partial class MainWindow : Window
{
public event delegato mydelegate;
public MainWindow()
{
InitializeComponent();
Persona p = new Persona();
p.Costa = "Paulo";
p.Cognome = "Sousa";
Animale a = new Animale("Fufficus", "Cane spaziale");
mydelegate = p.stampaPropUmano; // ??????? Why?
mydelegate += a.stampaPropAnimale;
}
private void button_Click(object sender, RoutedEventArgs e)
{
mydelegate(1);
}
}
}

That restriction is for clients of the class that declared the event, the class itself can use =. E.g
public delegate void delegato(int index=7);
public class Foo
{
public event delegato myEvent;
public void Bar()
{
// no error
myEvent = (int i) => {};
}
}
void Main()
{
var foo = new Foo();
// The event 'Foo.myEvent' can only appear on the left hand side of += or -= (except when used from within the type 'Foo')
foo.myEvent = (int i) => {};
}

A c# event is a multicast delegate. That is, a delegate with multiple targets.
All the event keyword does is ensure that classes that do not own the delegate can only use the += and -= operators upon the field.
By using the = operator, you are overwriting the value of delegato, assigning it to p.stampaPropUmano.

Well. Let's clarify it a bit more :
Delegates :
Action a = null;
a = MyMethod; // overwrites value of a with one method;
a += MyMethod; // assigns method to a (not changing anything apart of it)
a -= MyMethod; // removes method from a
Events :
Inside of class declaration :
public event Action MyEvent;
Inside of class ctor or any other method :
MyEvent = new Action(MyMethod); // assign the event with some delegate;
In same or any other class :
myClassInstance.MyEvent += new Action(MyOtherMethod); // subscribing for the event;
myClassInstance.MyEvent -= new Action(MyOtherMethod); // unsubscribing for the event;
So whenever you the event is fired it calls the method (or methods) on the delegate that is subscribed to it or is set explicitly inside the class where this event was created.
You may ask: why is not possible to assign value to event directly from other class?
Because it would be unsafe to use events in this case.
Let's assume that it's possible to assign some value for events from some other class and consider the scenario :
Class A has event - > MyEvent;
Class B subscribes for event with +=
Class C subscribes for event with +=
Class D changes MyEvent value to `null`
Event is invoked in Class A, but it's set to null and exception is thrown

Related

Assigning events in object initializer

Why isn't it possible to assign events along with properties in object initializers in C#? It seems to be so natural to do so.
var myObject = new MyClass()
{
Property = value,
Event1 = actor,
// or
Event2 += actor
};
Or is there some trick that I don't know of?
This didn't make C# 6 or C# 7 (since the original question), however it hasn't been decided against. There is an issue on GitHub that tracks the language proposal. You can vote for it there, as well as follow links into previous discussion around the feature.
https://github.com/dotnet/csharplang/issues/307
If you'd like to see this feature, add a thumbs-up to the issue to help raise its visibility.
The proposed syntax is:
var timer = new DispatcherTimer {
Tick += delegate {},
Interval = TimeSpan.FromSeconds(1d),
};
As far the external contract is concerned, an event doesn't have a setter, only add and remove methods - subscribers can register and unregister from the event, and the publishing object decides when to invoke the callbacks by 'raising' the event. Consequently, the idea of "assigning an event", in general, is meaningless.
However, when you declare an event in a class, the C# compiler provides you with what is really a convenience-feature: when you don't provide your own implementation, it creates a private, backing delegate-field for you, along with the appropriate add / remove implementations . This allows you to "set the event" (really the backing field) within the class, but not outside it. To understand this, consider:
public class Foo
{
// implemented by compiler
public event EventHandler MyEvent;
public static Foo FooFactory(EventHandler myEventDefault)
{
// setting the "event" : perfectly legal
return new Foo { MyEvent = myEventDefault };
}
}
public class Bar
{
public static Foo FooFactory(EventHandler myEventDefault)
{
// meaningless: won't compile
return new Foo { MyEvent = myEventDefault };
}
}
public class Baz
{
// custom implementation
public event EventHandler MyEvent
{
add { } // you can imagine some complex implementation here
remove { } // and here
}
public static Baz BazFactory(EventHandler myEventDefault)
{
// also meaningless: won't compile
return new Baz { MyEvent = myEventDefault };
}
}
You can only use the operators += or -= to an event outside its owner class.
public class Data
{
public event EventHandler OnSave = (s,e) =>
{
//do something important!
};
public void Save()
{
OnSave(this,null);
//do save
}
}
//outside the class
Data data = new Data { OnSave = null }; //compile error
data.OnSave = SomeMethodElse; //compile error
data.OnSave += MyCustomActionsOnSave; //ok
data.Save();
You can't remove the OnSave action defined in the class. You can only add/remove your own OnSave actions outside the class. If you remove the event keyword, the OnSave will be no longer an event, but an ordinary delegate. Then you can do anything including assigning value outside the class.

Why do we need the "event" keyword while defining events?

I don't understand why we need the event keyword while defining events, when we can do the same thing without using the event keyword, just by using delegates.
E.g.:
public delegate void CustomEventHandler(int a, string b);
public event CustomEventHandler customEvent;
customEvent += new CustomEventHandler(customEventHandler);
customEvent(1,"a"); // Raising the event
Here, if I remove the event keyword from the second line, then I can also raise the event by invoking the delegate.
Can anybody please tell me why this event keyword is needed?
Field-like events and public fields of delegate types look similar, but are actually very different.
An event is fundamentally like a property - it's a pair of add/remove methods (instead of the get/set of a property). When you declare a field-like event (i.e. one where you don't specify the add/remove bits yourself) a public event is created, and a private backing field. This lets you raise the event privately, but allow public subscription. With a public delegate field, anyone can remove other people's event handlers, raise the event themselves, etc - it's an encapsulation disaster.
For more on events (and delegates) read my article on this topic. (At some point I need to update this for C# 4, which changes field-like events very slightly. The gist of it is still correct though.)
The event keyword does 3 different things:
You can define an event in an interface, even though you cannot define regular fields in interfaces.
It changes the visibility of the = and () operators (assignment and invocation) to private, so that only the containing class can invoke the event or override all the methods contained in it. The -= and += operators can still be invoked on an event from outside the class defining it (they get the access modifier you wrote next to the event).
You can also override the way -= and += behave on events.
The other answers are fine; I'd just like to add something else to think about.
Your question is "why do we need events when we have fields of delegate type?" I would extend that question: why do you need methods, properties, events, instance constructors or finalizers if you have fields of delegate type? Why do you need anything other than fields that contain values and delegates in a type? Why not just say
class C
{
private int z;
public readonly Func<int, int> M = (int x)=>{ return x+z; }
// ... and so on
}
?
You don't need methods, properties or events. We give you that stuff because the method, property and event design patterns are important and useful, and deserve to have a standard, documented, clear way to implement them in the language.
It's partly needed because if you omit the event keyword, it breaks encapsulation. If it's just a public multicast delegate, anyone can invoke it, set it to null or tamper with it. If a class called MailNotifier exists and it has an event called MailReceived, it makes no sense for other types to be able to fire that event via calling mailNotifier.MailReceived();
On the other hand, you can only meddle with and invoke 'field like' events from the type that defined it.
If you wanted to keep your event invocation private, there's nothing to stop you doing something like this:
public class MyClassWithNonFieldLikeEvent
{
private CustomEventHandler m_delegate;
public void Subscribe(CustomEventHandler handler)
{
m_delegate += handler;
}
public void Unsubscribe(CustomEventHandler handler)
{
m_delegate -= handler;
}
private void DoSomethingThatRaisesEvent()
{
m_delegate.Invoke(...);
}
}
... but that's a whole load of code just to (more or less) do what field-like events already give us.
Events have distinct advantages compared to delegate-fields. Events can be defined in interfaces in contrast to fields, adding abstraction to the code, and even more importantly: Events can only be called from inside the defining class. In your case, anybody could call the event, possibly destroying your code.
See this blog post for further information.
delegate is a reference type. It inherit MulticastDelegate. event is a Modifier. event is a special modifier for delegate. It modifies some function/method accessibility, for example Invoke method. After modified by modifier event, a delegate instance become an new concept "Event". So Event is just one modified delegate. You cannot directly change reference or invoke an Event outside the class where the Event was defined, but you can change reference or invoke a normal delegate instance. Event provide additional protection, so that Event have more safety features.
When you are outside the class where the event was defined, your are allow to do two kind of operations to the Event, "+=" and "-=".
But you can do access all the public field, properties, methods etc. of a normal delegate instance.
Here is one example:
namespace DelegateEvent
{
//the following line behave as a class. It is indeed a reference type
public delegate void MyDelegate(string inputs);
//The following line is illegal. It can only be an instance. so it cannot be directly under namespace
//public event MyDelegate MyEvent;
public class MyClassA
{
public event MyDelegate MyEventA;
public MyDelegate MyDelegateA;
System.Threading.ManualResetEvent MyResetEvent = new System.Threading.ManualResetEvent(false);
public void TryToDoSomethingOnMyDelegateA()
{
if (MyDelegateA != null)
{
//User can assecc all the public methods.
MyDelegateA("I can invoke detegate in classA"); //invoke delegate
MyDelegateA.Invoke("I can invoke detegate in classA"); //invoke delegate
IAsyncResult result = MyDelegateA.BeginInvoke("I can invoke detegate in classA", MyAsyncCallback, MyResetEvent); //Async invoke
//user can check the public properties and fields of delegate instance
System.Reflection.MethodInfo delegateAMethodInfo = MyDelegateA.Method;
MyDelegateA = testMethod; //reset reference
MyDelegateA = new MyDelegate(testMethod); //reset reference
MyDelegateA = null; //reset reference
MyDelegateA += testMethod; //Add delegate
MyDelegateA += new MyDelegate(testMethod); //Add delegate
MyDelegateA -= testMethod; //Remove delegate
MyDelegateA -= new MyDelegate(testMethod); //Remove delegate
}
}
public void TryToDoSomethingOnMyEventA()
{
if (MyEventA != null)
{
MyEventA("I can invoke Event in classA"); //invoke Event
MyEventA.Invoke("I can invoke Event in classA"); //invoke Event
IAsyncResult result = MyEventA.BeginInvoke("I can invoke Event in classA", MyAsyncCallback, MyResetEvent); //Async invoke
//user can check the public properties and fields of MyEventA
System.Reflection.MethodInfo delegateAMethodInfo = MyEventA.Method;
MyEventA = testMethod; //reset reference
MyEventA = new MyDelegate(testMethod); //reset reference
MyEventA = null; //reset reference
MyEventA += testMethod; //Add delegate
MyEventA += new MyDelegate(testMethod); //Add delegate
MyEventA -= testMethod; //Remove delegate
MyEventA -= new MyDelegate(testMethod); //Remove delegate
}
}
private void MyAsyncCallback(System.IAsyncResult result)
{
//user may do something here
}
private void testMethod(string inputs)
{
//do something
}
}
public class MyClassB
{
public MyClassB()
{
classA = new MyClassA();
}
public MyClassA classA;
public string ReturnTheSameString(string inputString)
{
return inputString;
}
public void TryToDoSomethingOnMyDelegateA()
{
if (classA.MyDelegateA != null)
{
//The following two lines do the same job --> invoke the delegate instance
classA.MyDelegateA("I can invoke delegate which defined in class A in ClassB");
classA.MyDelegateA.Invoke("I can invoke delegate which defined in class A in ClassB");
//Async invoke is also allowed
//user can check the public properties and fields of delegate instance
System.Reflection.MethodInfo delegateAMethodInfo = classA.MyDelegateA.Method;
classA.MyDelegateA = testMethod; //reset reference
classA.MyDelegateA = new MyDelegate(testMethod); //reset reference
classA.MyDelegateA = null; //reset reference
classA.MyDelegateA += testMethod; //Add delegate
classA.MyDelegateA += new MyDelegate(testMethod); //Add delegate
classA.MyDelegateA -= testMethod; //Remove delegate
classA.MyDelegateA -= new MyDelegate(testMethod); //Remove delegate
}
}
public void TryToDoSomeThingMyEventA()
{
//check whether classA.MyEventA is null or not is not allowed
//Invoke classA.MyEventA is not allowed
//Check properties and fields of classA.MyEventA is not allowed
//reset classA.MyEventA reference is not allowed
classA.MyEventA += testMethod; //Add delegate
classA.MyEventA += new MyDelegate(testMethod); //Add delegate
classA.MyEventA -= testMethod; //Remove delegate
classA.MyEventA -= new MyDelegate(testMethod); //Remove delegate
}
private void testMethod(string inputs)
{
//do something here
}
}
}
Using the website sharplab.io you can actually decompile what the "event" keyword does.
Eg the following program:
using System;
using System.ComponentModel;
public class C {
public event EventHandler TestChanged;
public void M() {
}
}
Decompiles to the following:
using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Security;
using System.Security.Permissions;
using System.Threading;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.0.0.0")]
[module: UnverifiableCode]
public class C
{
[CompilerGenerated]
private EventHandler m_TestChanged;
public event EventHandler TestChanged
{
[CompilerGenerated]
add
{
EventHandler eventHandler = this.TestChanged;
while (true)
{
EventHandler eventHandler2 = eventHandler;
EventHandler value2 = (EventHandler)Delegate.Combine(eventHandler2, value);
eventHandler = Interlocked.CompareExchange(ref this.TestChanged, value2, eventHandler2);
if ((object)eventHandler == eventHandler2)
{
break;
}
}
}
[CompilerGenerated]
remove
{
EventHandler eventHandler = this.TestChanged;
while (true)
{
EventHandler eventHandler2 = eventHandler;
EventHandler value2 = (EventHandler)Delegate.Remove(eventHandler2, value);
eventHandler = Interlocked.CompareExchange(ref this.TestChanged, value2, eventHandler2);
if ((object)eventHandler == eventHandler2)
{
break;
}
}
}
}
public void M()
{
}
}
So you can literally write the same code as the above, it is just very wordy and prone to mistake. The event keyword handles this for you. Same as many other keywords like async etc. So it is really just syntactic sugar, that is all.
For fun try decompiling other keywords using sharplab.io to see. It is a great learning experience.

In C#, why can't I test if a event handler is null anywhere outside of the class that it's defined?

I am sure that I am just not understanding something fundamental about events and/or delegates in C#, but why can't I do the Boolean tests in this code sample:
public class UseSomeEventBase {
public delegate void SomeEventHandler(object sender, EventArgs e);
public event SomeEventHandler SomeEvent;
protected void OnSomeEvent(EventArgs e) {
// CANONICAL WAY TO TEST EVENT. OF COURSE, THIS WORKS.
if (SomeEvent != null) SomeEvent(this, e);
}
}
public class UseSomeEvent : UseSomeEventBase {
public bool IsSomeEventHandlerNull() {
// "LEFT HAND SIDE" COMPILER ERROR
return SomeEvent == null;
}
}
class Program {
static void Main(string[] args) {
var useSomeEvent = new UseSomeEvent();
useSomeEvent.SomeEvent +=new UseSomeEventBase.SomeEventHandler(FuncToHandle);
// "LEFT HAND SIDE" COMPILER ERROR
if (useSomeEvent.SomeEvent == null) {
}
var useSomeEventBase = new UseSomeEventBase();
useSomeEventBase.SomeEvent += new UseSomeEventBase.SomeEventHandler(FuncToHandle);
// "LEFT HAND SIDE" COMPILER ERROR
if (useSomeEventBase.SomeEvent == null) {
}
}
static void FuncToHandle(object sender, EventArgs e) { }
}
An event is really just an "add" operation and a "remove" operation. You can't get the value, you can't set the value, you can't call it - you can just subscribe a handler for the event (add) or unsubscribe one (remove). This is fine - it's encapsulation, plain and simple. It's up to the publisher to implement add/remove appropriately, but unless the publisher chooses to make the details available, subscribers can't modify or access the implementation-specific parts.
Field-like events in C# (where you don't specify the add/remove bits) hide this - they create a variable of a delegate type and an event. The event's add/remove implementations just use the variable to keep track of the subscribers.
Inside the class you refer to the variable (so you can get the currently subscribed delegates, execute them etc) and outside the class you refer to the event itself (so only have add/remove abilities).
The alternative to field-like events is where you explicitly implement the add/remove yourself, e.g.
private EventHandler clickHandler; // Normal private field
public event EventHandler Click
{
add
{
Console.WriteLine("New subscriber");
clickHandler += value;
}
remove
{
Console.WriteLine("Lost a subscriber");
clickHandler -= value;
}
}
See my article on events for more information.
Of course the event publisher can also make more information available - you could write a property like ClickHandlers to return the current multi-cast delegate, or HasClickHandlersto return whether there are any or not. That's not part of the core event model though.
You can easily use a very simple approach here to not repeatedly subscribe to an event.
Either of the 2 approaches below can be used:
Flag approach : _getWarehouseForVendorCompletedSubscribed is a private variable initialized to false.
if (!_getWarehouseForVendorCompletedSubscribed)
{
_serviceClient.GetWarehouseForVendorCompleted += new EventHandler<GetWarehouseForVendorCompletedEventArgs>(_serviceClient_GetWarehouseForVendorCompleted);
_getWarehouseForVendorCompletedSubscribed = true;
}
Unsubscribe Approach :Include an unsubscribe everytime you want to subscribe.
_serviceClient.GetWarehouseForVendorCompleted -= new
EventHandler<GetWarehouseForVendorCompletedEventArgs>
(_serviceClient_GetWarehouseForVendorCompleted);
_serviceClient.GetWarehouseForVendorCompleted += new
EventHandler<GetWarehouseForVendorCompletedEventArgs>
(_serviceClient_GetWarehouseForVendorCompleted);
Here the answer:
using System;
delegate void MyEventHandler();
class MyEvent
{
string s;
public event MyEventHandler SomeEvent;
// This is called to raise the event.
public void OnSomeEvent()
{
if (SomeEvent != null)
{
SomeEvent();
}
}
public string IsNull
{
get
{
if (SomeEvent != null)
return s = "The EventHandlerList is not NULL";
else return s = "The EventHandlerList is NULL"; ;
}
}
}
class EventDemo
{
// An event handler.
static void Handler()
{
Console.WriteLine("Event occurred");
}
static void Main()
{
MyEvent evt = new MyEvent();
// Add Handler() to the event list.
evt.SomeEvent += Handler;
// Raise the event.
//evt.OnSomeEvent();
evt.SomeEvent -= Handler;
Console.WriteLine(evt.IsNull);
Console.ReadKey();
}
}
Here's a slightly different question
What value is there in testing an externally defined event for null?
As an external consumer of an event you can only do 2 operations
Add a handler
Remove a handler
The null or non-nullness of the event has no bearing on these 2 actions. Why do you want to run a test which provides no perceivable value?
It's a rule in place when using the 'event' keyword. When you create an event, you are restricting outside class interaction with the delegate to a "subscribe / unsubscribe" relationship, this includes cases of inheritance. Remember an event is essentially a property, but for method calls, it isn't really an object itself, so really it looks more like this:
public event SomeEventHandler SomeEvent
{
add
{
//Add method call to delegate
}
remove
{
//Remove method call to delegate
}
}
You'd have to do that from the base class. That's the exact reason that you did this:
protected void OnSomeEvent(EventArgs e) {
// CANONICAL WAY TO TEST EVENT. OF COURSE, THIS WORKS.
if (SomeEvent != null) SomeEvent(this, e);
}
You can't access events from a derived class. Also, you should make that method virtual, so that it can be overridden in a derived class.
Publisher of the event implicitly overload only += and -= operations, and other operations are not implemented in the publisher because of the obvious reasons as explained above, such as don't want to give control to subscriber to change events.
If we want to validate if a particular event is subscribed in the subscriber class, better publisher will set a flag in its class when event is subscriber and clear the flag when it is unsubscriber.
If subscriber can access the flag of publisher, very easily identifiable whether the particular event is subscriber or not by checking the flag value.

How can I pass an event to a function in C#?

I am looking to pass an event to a helper function. This function will attach a method to the event. However, I am having trouble properly passing the event. I have tried passing a EventHandler<TEventArgs>. It compiles, but events are not attached (but are still added; it seems a copy of the event handler is made).
For example, if I have this:
public event EventHandler<EventArgs> MyEvent;
And the helper function:
public static void MyHelperFunction<TEventArgs>(EventHandler<TEventArgs> eventToAttachTo)
{
eventToAttachTo += (sender, e) => { Console.WriteLine("Hello world"); };
}
And the caller:
MyHelperFunction(MyEvent);
MyEvent(null, new EventArgs()); // Does nothing.
The reason why this does not work is += when applied to a delegate creates a new delegate which is the combination of the old and the new. It does not modify the existing delegate.
In order to get this to work you will have to pass the delegate by reference.
public static void Helper(ref EventHandler<EventArgs> e)
{
e+= (x,y) => {};
}
The reason this works outside of the method is because the LHS is still the actual field. So += will create a new delegate and assign back to the member field.
Just came up with this little helper. If it is your self-created Event you could use a wrapper like this. You can use your += operators to attach handlers as normal but can pass the wrapper around and even raise the event from elsewhere.
public class GenericEvent<T> where T:EventArgs
{
public event EventHandler<T> Source = delegate { };
public void Raise(object sender, T arg = default(T))
{
Source(sender, arg);
}
public void Raise(T arg = default(T))
{
Source(this, arg);
}
public void AddHandler(EventHandler<T> handler)
{
Source += handler;
}
public void RemoveHandler(EventHandler<T> handler)
{
Source -= handler;
}
public static GenericEvent<T> operator +(GenericEvent<T> genericEvent, EventHandler<T> handler)
{
genericEvent.AddHandler(handler);
return genericEvent;
}
}
Create the event like:
public GenericEvent<EventArgs> MyEvent = new GenericEvent<EventArgs>();
Attach handlers:
MyEvent += (s,e) => {};
Raise event:
MyEvent.Raise();
Just guessing: Have you tried passing it as ref?
public static void MyHelperFunction<TEventArgs>(ref EventHandler<TEventArgs> eventToAttachTo)
MyHelperFunction(ref MyEvent);
It's not exactly nice, but you can use reflection to do this.
public EventMonitor(object eventObject, string eventName)
{
_eventObject = eventObject;
_waitEvent = eventObject.GetType().GetEvent(eventName);
_handler = new EventHandler(SetEvent);
_waitEvent.AddEventHandler(eventObject, _handler);
}
Where eventObject is the object containing the event, and eventName is the name of the event.
SetEvent is your event handler.
I also have a dispose method like this:
public void Dispose()
{
_waitEvent.RemoveEventHandler(_eventObject, _handler);
}
I have a solution where I have an two interfaces. The first interface has methods for binding certain events, while the other interface has event methods that can be bound to those events.
The first interface's bind methods takes the second interface as parameter, which makes it possible to bind the events to the event methods of any class that implements the second interface.
Is that understandable, or would you prefer some code? :)
As many have pointed out, passing an event to a method is either not possible or not simple.
Please clarify, but I suspect your intended usage will look something like:
void Register()
{
var super = new SuperHandler();
//not valid syntax:
super.HandleEvent(MyEvent1);
super.HandleEvent(MyEvent2);
super.HandleEvent(MyEvent3);
super.HandleEvent(MyEvent4);
}
You can accomplish this simply by making your intended generic event handlers accessible publicly (or internally, if you desire):
public static class GenericHandler
{
public static void HandleAnyEvent(object sender, EventArgs e)
{
//handle
}
}
public class SomeClass
{
void RegisterEvents()
{
var r = new EventRaiser();
r.ImportantThingHappened += GenericHandler.HandleAnyEvent;
}
}
In this example my catch-all handler is in a static class, but you can just as well use a non-static class. Also, I see that in your example you have made the method generic (TEventArgs). Because all EventHandler derivatives (such as CancelEventHandler) match the base EventHandler, you do not need to involve generics (nor would it be helpful).
If the registration logic is complex or you must keep the EventHandler private, consider using Interface Events. This may not meet your intended goal of reducing the amount of code, but it will allow you to create a class that can predictably handle all of the events of a specific type.
interface IRaiseEvents
{
event EventHandler ConnectionCreated;
event EventHandler ConnectionLost;
}
public class SuperHandler
{
void RegisterEvents(IRaiseEvents raiser)
{
raiser.ConnectionCreated += (sender, args) => Console.WriteLine("Connected.");
raiser.ConnectionLost += (sender, args) => Console.WriteLine("Disconnected.");
}
}
Pass something like Action e = e => myevent += e;
And call from method with the handler? It has the benefit of working with .NET classes.

How do C# Events work behind the scenes?

I'm using C#, .NET 3.5. I understand how to utilize events, how to declare them in my class, how to hook them from somewhere else, etc. A contrived example:
public class MyList
{
private List<string> m_Strings = new List<string>();
public EventHandler<EventArgs> ElementAddedEvent;
public void Add(string value)
{
m_Strings.Add(value);
if (ElementAddedEvent != null)
ElementAddedEvent(value, EventArgs.Empty);
}
}
[TestClass]
public class TestMyList
{
private bool m_Fired = false;
[TestMethod]
public void TestEvents()
{
MyList tmp = new MyList();
tmp.ElementAddedEvent += new EventHandler<EventArgs>(Fired);
tmp.Add("test");
Assert.IsTrue(m_Fired);
}
private void Fired(object sender, EventArgs args)
{
m_Fired = true;
}
}
However, what I do not understand, is when one declares an event handler
public EventHandler<EventArgs> ElementAddedEvent;
It's never initialized - so what, exactly, is ElementAddedEvent? What does it point to? The following won't work, because the EventHandler is never initialized:
[TestClass]
public class TestMyList
{
private bool m_Fired = false;
[TestMethod]
public void TestEvents()
{
EventHandler<EventArgs> somethingHappend;
somethingHappend += new EventHandler<EventArgs>(Fired);
somethingHappend(this, EventArgs.Empty);
Assert.IsTrue(m_Fired);
}
private void Fired(object sender, EventArgs args)
{
m_Fired = true;
}
}
I notice that there is an EventHandler.CreateDelegate(...), but all the method signatures suggest this is only used for attaching Delegates to an already existing EventHandler through the typical ElementAddedEvent += new EventHandler(MyMethod).
I'm not sure if what I am trying to do will help... but ultimately I'd like to come up with an abstract parent DataContext in LINQ whose children can register which table Types they want "observed" so I can have events such as BeforeUpdate and AfterUpdate, but specific to types. Something like this:
public class BaseDataContext : DataContext
{
private static Dictionary<Type, Dictionary<ChangeAction, EventHandler>> m_ObservedTypes = new Dictionary<Type, Dictionary<ChangeAction, EventHandler>>();
public static void Observe(Type type)
{
if (m_ObservedTypes.ContainsKey(type) == false)
{
m_ObservedTypes.Add(type, new Dictionary<ChangeAction, EventHandler>());
EventHandler eventHandler = EventHandler.CreateDelegate(typeof(EventHandler), null, null) as EventHandler;
m_ObservedTypes[type].Add(ChangeAction.Insert, eventHandler);
eventHandler = EventHandler.CreateDelegate(typeof(EventHandler), null, null) as EventHandler;
m_ObservedTypes[type].Add(ChangeAction.Update, eventHandler);
eventHandler = EventHandler.CreateDelegate(typeof(EventHandler), null, null) as EventHandler;
m_ObservedTypes[type].Add(ChangeAction.Delete, eventHandler);
}
}
public static Dictionary<Type, Dictionary<ChangeAction, EventHandler>> Events
{
get { return m_ObservedTypes; }
}
}
public class MyClass
{
public MyClass()
{
BaseDataContext.Events[typeof(User)][ChangeAction.Update] += new EventHandler(OnUserUpdate);
}
public void OnUserUpdated(object sender, EventArgs args)
{
// do something
}
}
Thinking about this made me realize I don't really understand what's happening under the hod with events - and I would like to understand :)
I've written this up in a fair amount of detail in an article, but here's the summary, assuming you're reasonably happy with delegates themselves:
An event is just an "add" method and a "remove" method, in the same way that a property is really just a "get" method and a "set" method. (In fact, the CLI allows a "raise/fire" method as well, but C# never generates this.) Metadata describes the event with references to the methods.
When you declare a field-like event (like your ElementAddedEvent) the compiler generates the methods and a private field (of the same type as the delegate). Within the class, when you refer to ElementAddedEvent you're referring to the field. Outside the class, you're referring to the field.
When anyone subscribes to an event (with the += operator) that calls the add method. When they unsubscribe (with the -= operator) that calls the remove.
For field-like events, there's some synchronization but otherwise the add/remove just call Delegate.Combine/Remove to change the value of the auto-generated field. Both of these operations assign to the backing field - remember that delegates are immutable. In other words, the autogenerated code is very much like this:
// Backing field
// The underscores just make it simpler to see what's going on here.
// In the rest of your source code for this class, if you refer to
// ElementAddedEvent, you're really referring to this field.
private EventHandler<EventArgs> __ElementAddedEvent;
// Actual event
public EventHandler<EventArgs> ElementAddedEvent
{
add
{
lock(this)
{
// Equivalent to __ElementAddedEvent += value;
__ElementAddedEvent = Delegate.Combine(__ElementAddedEvent, value);
}
}
remove
{
lock(this)
{
// Equivalent to __ElementAddedEvent -= value;
__ElementAddedEvent = Delegate.Remove(__ElementAddedEvent, value);
}
}
}
The initial value of the generated field in your case is null - and it will always become null again if all subscribers are removed, as that is the behaviour of Delegate.Remove.
If you want a "no-op" handler to subscribe to your event, so as to avoid the nullity check, you can do:
public EventHandler<EventArgs> ElementAddedEvent = delegate {};
The delegate {} is just an anonymous method which doesn't care about its parameters and does nothing.
If there's anything that's still unclear, please ask and I'll try to help!
Under the hood, events are just delegates with special calling conventions. (For example, you don't have to check for nullity before raising an event.)
In pseudocode, Event.Invoke() breaks down like this:
If Event Has Listeners
Call each listener synchronously on this thread in arbitrary order.
Since events are multicast, they will have zero or more listeners, held in a collection. The CLR will loop through them, calling each in an arbitrary order.
One big caveat to remember is that event handlers execute in the same thread as the event is raised in. It's a common mental error to think of them as spawning a new thread. They do not.

Categories

Resources