In order to better understand, I wanted to know how event differs from delegate and also the use of + = and - = operators for methods in C #

2 answers 2

Let's start by saying that event and delegate are two different things, as different as a car and a carrot. The difference between the delegate field and the event is approximately the same as between the field and the property: event sometimes looks like a delegate field. Let's figure it out.

delegate is a class that contains a “pattern” of a method, that is, a method signature. The delegate type variable is an object of type MulticastDelegate (more precisely, derived from it), which may contain one or several objects representing methods with a signature compatible with the “template” (counter- and covariance complicates the picture a little). That is, it is like a variable that may contain functions . For such variables, the operation + defined, which combines the terms-functions into one new function, and the symmetric operation - . These operations automatically generate derivative operations += and -= .

event is just a pair of methods in the class, denoted as add and remove , and having arbitrary semantics chosen by the programmer. (The analogue is a getter and a setter property.) In the default implementation for event, a hidden delegate type field is created, and add / remove add or remove methods from it (under lock). (To confuse the picture a little, this hidden field is available by the same name as the event .) The add / remove functions that make up the event are called respectively as += and -= . No operations + / - , of course, no.


Let's go over the difference in the event class in the class from the public field of the delegate type.

Consider the case when event is implemented "by default", that is, with an implicit delegate field. The difference is that:

1) For the delegate field you have full access to it. You can - also outside the class! - parse MulticastDelegate into parts and assemble a new one, you can replace it with your own or assign it null , you can call it, you can copy it to yourself in a variable. You have full access, as well as to any public field. (This, of course, flagrantly violates encapsulation.)

For event, you can only write instance.Event += handler and instance.Event -= handler , which maps to the add and remove functions, which in turn call += and -= again for an automatically implemented delegate. You have no other access. Inside the class, however, you can get the value of this delegate for reading, using the name of the event. (This is necessary, for example, in order to call this delegate; VB, unlike C #, has a special RaiseEvent method with the necessary checks.)

2) Another subtle difference (already mentioned in the comments to other answers) is that add / remove are called under lock'om, which makes them thread-safe. However, the event's thread safety is very complicated (almost said “impossible in principle”) (see article [2]), so this difference is not essential when properly programmed: operations with event should usually occur only in one thread. Note that lock is not available outside add / remove , so you cannot copy the delegate to a local variable under it for a thread-safe call *:

 void Raise() { EventHandler copy; lock (<sorry, not available>) copy = MyEvent; if (copy != null) copy(...); } 

For the case when the event is not implemented by default, there is practically nothing in common. The add and remove methods can do anything:

 int subscriberCount = 0; public event EventHandler MyEvent { add { subscriberCount++; } remove { subscriberCount--; } } 

A programmer can start a delegate independently and “add” signed handlers there, but in principle this is his goodwill. On the other hand, it is still recommended not to break the class semantics expected by customers, and to use event as intended.

Literature:

  1. Jon Skeet: Delegates and Events
  2. Eric Lippert: Events and races

In addition, the delegate keyword is used to declare an anonymous function (the older parallel syntax for lambda expressions ).


* In modern versions of C #, the MyEvent?.Invoke(...) pattern is recommended for a thread-safe call. However, with thread safety, events are not all smooth, as explained in article [2].

  • five
    For those who know the delegates: try to guess what the code will display (and if you don’t guess, think why): Action a1 = () => Console.Write (1); Action a2 = () => Console.Write (2); Action a3 = () => Console.Write (3); ((a1 + a2 + a3) - (a1 + a2)) (); ((a1 + a2 + a3) - (a1 + a3)) (); - VladD
  • It is interesting. Thanks for the example - Shad
  • An example is really interesting. But that "Resharper writes about this": confluence.jetbrains.com/display/ReSharper/ ... - wind
  • @wind: oh, they have the same example! :-) As for the “unpredictable semantics,” they turned up a bit. If you sign and unsubscribe the same MulticastDelegate in the stack order (the first subscribed - the last unsubscribed), everything will work with the guarantee correctly. If not, errors are possible: ((a2 + (a1 + a2)) - a2) - (a1 + a2) is not empty. If a single delegate is signed-unsubscribed all the time, there will be no problems guaranteed. - VladD
  • 3
    @wind: And that's what they write about R # on SO : Jon Skeet: I think it’s unpredictable. They're very clearly specified. The same thing as “unpredictable”. It is the exact same issue . - VladD

The main difference between event and delegate is that event can only be run in the class in which it is declared. In addition, if there is an event, the compiler creates not only the corresponding private delegate field, but also two open methods for subscribing and canceling it for events.

Of the smaller differences, it is worth remembering that an event, unlike a delegate, can be a member of an interface. This is because delegates are always fields, while interfaces cannot contain fields. In addition, an event, unlike a delegate, cannot be a local variable in a method.

In general, about the events and their relationship with delegates read Richter, Chapter 11, which is called "Events"

  • It is also worth saying that the methods automatically created by the compiler for adding and removing event handlers are thread-safe (if they are not overridden by add / remove). - Shad
  • Hmm, what about interface I {Action a {get; set; }}? - VladD
  • Operations + = / - = for this delegate property will be performed without lock'a - Shad
  • one
    @Shad: yes, but we got a delegate in the interface. :-) - VladD
  • @VladD, and I thought that the question is connected with thread-safety and addressed to me :-) - Shad