What is meaning of `event = method` in C# - c#

I am familiar with events and delegates in C#, and I have been using them like following for quite some time
// Subscribing
SomeEvent += SomeMethod;
// Unsubscribing
SomeEvent -= SomeMethod;
But, I have seen something like following
SomeEvent = SomeMethod
How it is different from subscribing to an event. In above code SomeMethod gets called when SomeEvent is raised, but how it is different from subscribing to an event, what it does behind the scene?
Edit
Based on feedback from #Eldar and #PMF I found out that SomeEvent = SomeMethod is called single cast delegate usage or Unicast usage.
To understand it properly I quickly setup a test script in Unity
public class Testing : MonoBehaviour
{
// UnityAction is a simple delegate
// public delegate void UnityAction();
private UnityAction OnTestAction;
private void Start()
{
OnTestAction = OnTestActionHandler;
}
private void OnTestActionHandler()
{
Debug.Log("On Test Action Handler");
}
// Update is basically infinite loop in unity
private void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
OnTestAction?.Invoke();
}
if (Input.GetKeyDown(KeyCode.A))
{
OnTestAction -= OnTestActionHandler;
}
}
}
In above code I found out that it works same as Subscribing and Unsubscribing, So can we use += instead of = or is it bad to use += when you only have to subscribe event to single method?

Note that only field-like events can be used in this way:
SomeEvent = SomeMethod;
SomeEvent += SomeMethod; and SomeEvent -= SomeMethod; on the other hand can be used with all events, and they call the add and remove accessors of SomeEvent respectively.
Here's what a field-like event is, from the language spec:
Within the program text of the class or struct that contains the declaration of an event, certain events can be used like fields. To be used in this way, an event [...] must not explicitly include event_accessor_declarations. Such an event can be used in any context that permits a field. The field contains a delegate which refers to the list of event handlers that have been added to the event. If no event handlers have been added, the field contains null.
In general, a field like event does not have explicit add and remove accessors.
Notice how they have a delegate-typed field storing the list of event handlers. SomeEvent = SomeMethod; basically sets the field to a delegate whose invocation list contains only SomeMethod. This means that all the previous handlers in the list are discarded. If you are wondering how a method name can be converted to a delegate, see method group conversions.
The add and remove accessors of field-like events are automatically generated:
When compiling a field-like event, the compiler automatically creates storage to hold the delegate, and creates accessors for the event that add or remove event handlers to the delegate field. The addition and removal operations are thread safe, and may (but are not required to) be done while holding the lock on the containing object for an instance event, or the type object for a static event.
The spec doesn't say exactly how, so that's up to the implementation to decide. On SharpLab, we can see that one implementation would be:
private EventHandler m_SomeEvent;
private event EventHandler SomeEvent
{
[CompilerGenerated]
add
{
EventHandler eventHandler = this.SomeEvent;
while (true)
{
EventHandler eventHandler2 = eventHandler;
EventHandler value2 = (EventHandler)Delegate.Combine(eventHandler2, value);
eventHandler = Interlocked.CompareExchange(ref this.SomeEvent, value2, eventHandler2);
if ((object)eventHandler == eventHandler2)
{
break;
}
}
}
[CompilerGenerated]
remove
{
EventHandler eventHandler = this.SomeEvent;
while (true)
{
EventHandler eventHandler2 = eventHandler;
EventHandler value2 = (EventHandler)Delegate.Remove(eventHandler2, value);
eventHandler = Interlocked.CompareExchange(ref this.SomeEvent, value2, eventHandler2);
if ((object)eventHandler == eventHandler2)
{
break;
}
}
}
}
Note the field m_SomeEvent, and the calls to Delegate.Remove and Delegate.Combine - those are what adds the new handler to the list of handlers, and removes a handler from the list of handlers.
So can we use += instead of = or is it bad to use += when you only have to subscribe event to single method?
It's not bad to use += when you subscribe to only one method. In fact, I would recommend you to use +=. You may say "I only want to subscribe to a single method" now, but you never know when that's going to change :)

Related

Why can´t we raise event with accessors?

Why can´t we raise an event with a custom implementation, while it is possible without them? See this code:
public class Program
{
private EventHandler myEvent;
public event EventHandler MyEvent
{
add { myEvent += value; }
remove { myEvent -= value; }
}
public event EventHandler AnotherEvent;
public static void Main()
{
var target = new Program();
target.MyEvent(null, null); // ERROR CS0079
target.AnotherEvent(null, null); // compiles
}
}
You see both events are declared within my class. While target.AnotherEvent(...) compiles just fine, target.MyEvent(...) does not:
The Event MyEvent can only appear on the left hand side of += or -=.
I Know an event is just a delegate with an add- and remove-method. So AnotherEvent is translated by the compiler to an add- and a remove-method:
private EventHandler _AnotherEvent;
public event EventHandler AnotherEvent
{
add { _AnotherEvent += value; }
remove { _AnotherEvent -= value; }
}
So I assume the call to AnotherEvent is replaced by the compiler to a call to the private delegate, which was _AnotherEvent(...).
Did I get this right? Are there any docs about why the second call works while the former does not? Or at least any description about what the compiler does here?
When an auto event is used public event EventHandler AnotherEvent;. The compiler will create a field (and some methods) for it and invoking is done on that field. So the public event does not exists anymore. It's syntactic sugar.
So invoking a non-auto event is not possible. Because it isn't found in the compiled code. It's replaced by add_, remove_ methods. You can only invoke on the private field (which is generated)
This explains why you cannot invoke an event outside the class instance.
It doesn't work because there is simply now way to get the actual invokeable event handler. As you have noted, there is just an add and remove, not a get.
The generated code for the event handler is:
.event [mscorlib]System.EventHandler MyEvent
{
.addon instance void ConsoleApp1.Program::add_MyEvent(class [mscorlib]System.EventHandler)
.removeon instance void ConsoleApp1.Program::remove_MyEvent(class [mscorlib]System.EventHandler)
} // end of event Program::MyEvent
It adds two method references, one for add and one for remove. If you look at it, how would it know what method to invoke? What if add and remove are much more complex than they are now? There is just no way to know for sure what event handler to call.
It's syntactical sugar. That you can call AnotherEvent like the backing field is a convenience provided by the compiler (AnotherEvent is a so-called field-like event). Once you add your own accessors, the event declaration ceases to be a field-like event and has to be invoked through its backing field.
See the relevant part of the C# Language Specification:
Field-like events
Within the program text of the class or struct that contains the declaration of an event, certain events can be used like fields. To be
used in this way, an event must not be abstract or extern, and must
not explicitly include event_accessor_declarations. Such an event can
be used in any context that permits a field. The field contains a
delegate (Delegates) which refers to the list of event handlers that
have been added to the event. If no event handlers have been added,
the field contains null.
(emphasis mine)
It is recommended that you lock the event before you add or remove a new event handler method.
saying that, have a look on this piece of code:
public event EventHandler MyEvent
{
add
{
lock (objectLock)
{
myEvent += value;
}
}
remove
{
lock (objectLock)
{
myEvent -= value;
}
}
}
The reason public event EventHandler AnotherEvent; works is because When no custom event accessors are supplied in your code, the compiler will add them automatically.
Follow this doc, How to: Implement Custom Event Accessors in order to get more details about the proper implementation and this post for another source.
Regarding the implementation:
private EventHandler myEvent;
public event EventHandler MyEvent
{
add
{
lock (objectLock)
{
myEvent += value;
}
}
remove
{
lock (objectLock)
{
myEvent -= value;
}
}
}
public event EventHandler AnotherEvent;
public static void Main()
{
var target = new Program();
var myEvent = target.MyEvent;
myEvent?.Invoke(EventArgs.Empty, EventArgs.Empty);
target.AnotherEvent(null, null);
}
Edit to explain the implementation:
var myEvent = target.MyEvent;
With an explicit event, you have to provide your own backing store - either a delegate field or something like EventHandlerList, so we just go with var here.

how to detect event handler and released it?

Suppose I have a class say a viewmode class mvvm. Then there are some event handlers created for this vm. then it could be used by many others with different situation.
So if I have an instance of myvm, I want to detect if there is any event handler hooked up and want to release it for memory issue.
What's the generic way to do this out of myvm, for example, I may not have the source code of myvm?
Events are designed such that code outside the class that declared them cannot get access to the underlying delegate. For example, according to Section "10.8 Events" in the C# Language specification (emphasis mine):
In an operation of the form x += y or x -= y, when x is an event and
the reference takes place outside the type that contains the
declaration of x, the result of the operation has type void (as
opposed to having the type of x, with the value of x after the
assignment). This rule prohibits external code from indirectly
examining the underlying delegate of an event.
Therefore, finding out what is subscribed to the event outside the class may be, at best, a "work around".
If you have access to the source of the class containing the event and you want to keep track of delegates hooked up to an event, implement the add and remove keyword in the event definition and manually keep track of them in a Dictionary.
If I understand you correctly.
This class wraps the unknown myvm class which I use SocketAsyncEventArgs to illustrate, cos obviously we don't have the source code for SocketAsyncEventArgs class.
And I wrapped the Completed event of SocketAsyncEventArgs class. When that event is triggered, _instance_Completed will be fired, then _myvm event will be fired. So what we need to do is subscribe/unsubscribe _myvm event.
Then I leave an event for people to subscribe/unsubscribe _myvm event, as subscribing/unsubscribing, the delegates are stored into a List therefore you can clear the
by call the ClearEvents() method.
Hope it will help.
public class WrapperClass
{
private EventHandler<SocketAsyncEventArgs> _myEvent;
private SocketAsyncEventArgs _myvm;
private List<Delegate> delegates;
public WrapperClass()
{
delegates = new List<Delegate>();
}
public void SetInstance(SocketAsyncEventArgs myvm)
{
_myvm = myvm;
_myvm.Completed += new EventHandler<SocketAsyncEventArgs>(_instance_Completed);
}
private void _instance_Completed(object sender, SocketAsyncEventArgs e)
{
if (_myEvent != null)
{
_myEvent(sender, e);
}
}
public event EventHandler<SocketAsyncEventArgs> myEvent
{
add
{
delegates.Add(value);
_myEvent = (EventHandler<SocketAsyncEventArgs>)Delegate.Combine(_myEvent, value);
}
remove
{
delegates.Remove(value);
_myEvent = (EventHandler<SocketAsyncEventArgs>)Delegate.Remove(_myEvent, value);
}
}
public void ClearEvents()
{
foreach (var d in delegates)
{
_myEvent = (EventHandler<SocketAsyncEventArgs>)Delegate.Remove(_myEvent, d);
}
}
}

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.

C#: event with explicity add/remove != typical event?

I have declared a generic event handler
public delegate void EventHandler();
to which I have added the extension method 'RaiseEvent':
public static void RaiseEvent(this EventHandler self) {
if (self != null) self.Invoke();
}
When I define the event using the typical syntax
public event EventHandler TypicalEvent;
then I can call use the extension method without problems:
TypicalEvent.RaiseEvent();
But when I define the event with explicit add/remove syntax
private EventHandler _explicitEvent;
public event EventHandler ExplicitEvent {
add { _explicitEvent += value; }
remove { _explicitEvent -= value; }
}
then the extension method does not exist on the event defined with explicit add/remove syntax:
ExplicitEvent.RaiseEvent(); //RaiseEvent() does not exist on the event for some reason
And when I hover over to event to see the reason it says:
The event 'ExplicitEvent' can only
appear on the left hand side of += or
-=
Why should an event defined using the typical syntax be different from an event defined using the explicit add/remove syntax and why extension methods do not work on the latter?
EDIT: I found I can work around it by using the private event handler directly:
_explicitEvent.RaiseEvent();
But I still don't understand why I cannot use the event directly like the event defined using the typical syntax. Maybe someone can enlighten me.
When you create a "field-like" event, like this:
public event EventHandler Foo;
the compiler generates a field and an event. Within the source code of the class which declares the event, any time you refer to Foo the compiler understand that you're referring to the field. However, the field is private, so any time you refer to Foo from other classes, it refers to the event (and therefore the add/remove code).
If you declare your own explicit add/remove code, you don't get an auto-generated field. So, you've only got an event, and you can't raise an event directly in C# - you can only invoke a delegate instance. An event isn't a delegate instance, it's just an add/remove pair.
Now, your code contained this:
public EventHandler TypicalEvent;
This is slightly different still - it wasn't declaring an event at all - it was declaring a public field of the delegate type EventHandler. Anyone can invoke that, because the value is just a delegate instance. It's important to understand the difference between a field and an event. You should never write this kind of code, just as I'm sure you don't normally have public fields of other types such as string and int. Unfortunately it's an easy typo to make, and a relatively hard one to stop. You'd only spot it by noticing that the compiler was allowing you to assign or use the value from another class.
See my article on events and delegates for more information.
Because you can do this (it's non-real-world sample, but it "works"):
private EventHandler _explicitEvent_A;
private EventHandler _explicitEvent_B;
private bool flag;
public event EventHandler ExplicitEvent {
add {
if ( flag = !flag ) { _explicitEvent_A += value; /* or do anything else */ }
else { _explicitEvent_B += value; /* or do anything else */ }
}
remove {
if ( flag = !flag ) { _explicitEvent_A -= value; /* or do anything else */ }
else { _explicitEvent_B -= value; /* or do anything else */ }
}
}
How can the compiler know what it should do with "ExplicitEvent.RaiseEvent();"?
Answer: It can't.
The "ExplicitEvent.RaiseEvent();" is only syntax sugar, which can be predicated only if the event is implicitly implemented.
That's because you're not looking at it right.
The logic is the same as in Properties.
Once you've set the add/remove it's no longer an actual event, but a wrapper to expose the actual event (events can only be triggered from inside the class itself, so you always have access locally to the real event).
private EventHandler _explicitEvent;
public event EventHandler ExplicitEvent {
add { _explicitEvent += value; }
remove { _explicitEvent -= value; }
}
private double seconds;
public double Hours
{
get { return seconds / 3600; }
set { seconds = value * 3600; }
}
In both cases the member with the get/set or add/remove property doesn't really contain any data. You need a "real" private member to contain the actual data.
The properties just allow you program extra logic when exposing the members to outside world.
A good example for WHY you'd want to do it, is to stop extra computation when it's not needed (no one is listening to the event).
For example, lets say the events are triggered by a timer, and we don't want the timer to work if no-one is registered to the event:
private System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();
private EventHandler _explicitEvent;
public event EventHandler ExplicitEvent
{
add
{
if (_explicitEvent == null) timer.Start();
_explicitEvent += value;
}
remove
{
_explicitEvent -= value;
if (_explicitEvent == null) timer.Stop();
}
}
You'd probably want to lock the add/remove with an object (an afterthought)...
The "plain" declaration for TypicalEvent does some compiler trickery. It creates an event metadata entry, add and remove methods and a backing field. When your code refers to TypicalEvent, the compiler translates it into a reference to the backing field; when external code refers to TypicalEvent (using += and -=), the compiler translates it into a reference to the add or remove method.
The "explicit" declaration bypasses this compiler trickery. You are spelling out the add and remove methods and the backing field: indeed, as TcKs points out, there may not even be a backing field (this is a common reason for using the explicit form: see e.g. events in System.Windows.Forms.Control). Therefore the compiler can no longer quietly translate the reference to TypicalEvent into a reference to the backing field: if you want the backing field, the actual delegate object, you have to reference the backing field directly:
_explicitEvent.RaiseEvent()

Categories

Resources