Delving into Troelsen’s book about delegates, he noticed such a funny thing that delegates can be used in the manner of events and have my own question in my delegate registrar method that looks like this

public class Car { // 1. Определить тип делегата. public delegate void CarEngineHandler(string msgForCaller); // 2. Определить переменную-член типа этого делегата. private CarEngineHandler listOfHandlers; // 3. Добавить регистрационную функцию для вызывающего кода. public void RegisterWithCarEngine(CarEngineHandler methodToCall) { listOfHandlers = methodToCall; } } 

Here, the RegisterWithCarEngine method takes the delegate object as a parameter and then further defines the method for invoking the delegate.

 // 4. Реализовать метод Accelerate() для обращения // к списку вызовов делегата при нужных условиях. public void Accelerate(int delta) { // Если этот автомобиль сломан, отправить сообщение об этом. if (carIsDead) { if (listOfHandlers != null) listOfHandlers("Sorry, this car is dead..."); } else { CurrentSpeed += delta; // Автомобиль почти сломан? if (10 == (MaxSpeed - CurrentSpeed) && listOfHandlers != null) { listOfHandlers("Careful buddy! Gonna blow!"); } if (CurrentSpeed >= MaxSpeed) carIsDead = true; else Console.WriteLine("CurrentSpeed= {0}", CurrentSpeed); } } 

And after all this, in the main, we call the method for registering the delegate, but this was not done in a usual way for me in the usual way as it passes an anonymous delegate object (by calling the constructor and passing it the method name), which is then assigned to the field that is declared in the class for storing information about what method will be called when an event occurs (here, as if by delegates, this is done not to be confused with events) and this is the actual code that caused my question and I would like to clarify whether an anonymous object is transmitted correctly t delegate

 class Program { static void Main(string[] args) { Console.WriteLine("***** Delegates as event enablers *****\n"); // Сначала создать объект Car. Car cl = new Car("SlugBug", 100, 10); // Теперь сообщить ему, какой метод вызывать, // когда он захочет отправить сообщение. cl.RegisterWithCarEngine(new Car.CarEngineHandler(OnCarEngineEvent)); // Ускорить (это инициирует события). Console.WriteLine("***** Speeding up *****"); for (int i = 0; i < 6; i++) cl.Accelerate(20); Console.ReadLine(); } // Это цель для входящих сообщений. public static void OnCarEngineEvent(string msg) { Console.WriteLine("\n***** Message From Car Object *****"); Console.WriteLine("=> {0}", msg); Consоle.WriteLine("* * *\n"); } } 

That directly absolutely specifically that line with a call of a method RegisterWithCarEngine and its argument which actually is a call of a method new that exactly here will occur is interested. Type creating a delegate object with no name and then we will pass it to the field that is in each object of the Car class and thus write down what method to call (I would be more understandable if we say passed the delegate variable, but here it’s done and I want to clarify whether got it)

  • format the code, it is impossible to read the same - tym32167
  • I did not notice the moment, I did not notice it in all places - Mark
  • tym himself also understand that cuts his eyes when those curly brackets are not so worth it and I would like everything to be set up like in the studio, but I don’t know how to bring a code from the book into a decent look not so long ago tell me - Mark
  • CarEngineHandler is just the delegate signature, not the delegate itself. That is, you describe in it what arguments and what return value. new Car.CarEngineHandler(OnCarEngineEvent)) is already directly creating a delegate that will call the OnCarEngineEvent method every time the delegate itself is called. At the time of calling the RegisterWithCarEngine method, as in the call of any method, everything that is in the arguments is first calculated, and then it is sent to the method. In this case, the delegate is first created, and the already-prepared delegate is sent further as an argument. - John

2 answers 2

In modern object-oriented languages, the rule is that everything is an object . This applies to Smalltalk, Java, and C #.

That is why integers in C # are not primitive entities, but objects of the class System.Int32 . The same with pointers to functions that occur in C and C ++. For each such pointer, the compiler implicitly generates a descendant class from System.Delegate .

Take a look at the line:

 cl.RegisterWithCarEngine(new Car.CarEngineHandler(OnCarEngineEvent)); 

Here you create a new object of the implicitly generated class Car.CarEngineHandler . Please note that you did not explicitly describe this class anywhere; you wrote:

 public delegate void CarEngineHandler(string msgForCaller); 

and the compiler generated the required class and a suitable implementation of several methods, in particular, Invoke .

However, as a syntactic sugar, you can not explicitly create an object and write short code:

 cl.RegisterWithCarEngine(OnCarEngineEvent); 

But in this case, the compiler will still do the same thing that you wrote manually above.

That is, yes, a delegate object will be created in your code that will contain a single method address and this object will be copied into the listOfHandlers field.

Now about what delegates are. Historically, when C # was invented, it was assumed that delegates would be used to implement the event mechanism, that is, several subscriber methods could be stored in them. It seemed so that there would be single delegates (single cast delegates), which store only one method address, and multiple delegates (multi cast delegates), in which many method addresses are stored.

Then it turned out that this complicates things, and the delegates eventually made them universal. There are no single delegates in .NET, many delegates can always be stored in each delegate. It should probably be recalled here that if you use a class method (static), then only the address of the method is stored, and if an instance method, then both the address of the method and the address of the signed object are stored.

Now how delegates and events are related. Approximately the same as fields and properties. You have full access to the class field: you can read it, change it, get its address. Properties close direct access, and you of all opportunities get only the opportunity to read and write, and even then not necessarily. This allows you not only to hide and encapsulate the implementation, but also allows you to use multiple inheritance of interfaces in the language, due to the fact that there are no fields in the interface, but only abstract virtual methods.

Likewise, an event (event) restricts access to a delegate to two operations: adding a new method and removing an existing method, add and remove . You can no longer, for example, trigger an event outside the class (only from the inside), and you cannot clear it completely, although if it were a delegate field, you could even call it outside.

Well, as in the case of properties, you can describe events in interfaces, but you cannot describe delegate fields in them.

Finally, the last, about operations += and -= . This is also syntactic sugar. Delegates are immutable objects, so you cannot add another method to the delegate ( += ). When you write like this, the compiler calls the static Delegate.Combine method, which creates a new delegate from the old delegate and your method. But for the += event, the compiler will call the implicitly generated add method for this event.

  • Mark Shevchenko (I hope I wrote your name correctly), I’m just tormented by the question that in the method where we actually register the delegate for which the delegate object is specified in the parameters, we transfer to it not a specific object but pass new as it were to the delegate and I wanted to know Is it true that what I am saying now? I just saw that you can specify a method for the delegate through the constructor passing its name in this way: suppose we have a delegate type Handler and this expression goes Handler hnd1 = new Handler (Method_Name); And here is just taken part from the new - Mark
  • @Mark, OnCarEngine is not a delegate, it is a method. You cannot pass it, because in C # everything must be an object, and the address of the method is not an object. So you need to wrap the address of the method in the delegate object. Yes, he is nameless, but it's not scary. - Mark Shevchenko
  • Bozhechki my thanks to you, I wanted to hear it, but it’s unclear how 2-3 hours it took to argue with a friend of tym who didn’t have a studio or another online ide in order to check whether it worked at all just saying that it’s not working. The whole problem for me was that we transferred not a specific object but the one that was created by the new operator and it looked not very familiar to the eyes - Mark
  • one
    @Mark didn’t argue with anyone, I just pointed out a typo in your question, complained about poor code generation and corrected this formatting to you so that more people would pay attention to your question. - tym32167 5:02 pm
  • Comrade tym, I have already said that it is not a typo, but if you can’t stop it, I will test my strength and try to parry. 1) The delegate is a reference type and therefore when in the registration method itself assignment of one delegate variable to another occurred, it received a reference to an object in which references to methods that need to be called 2) Thus, we can pass one or several methods to which the delegate refers 3) Your + = as I said before will allow you to add new methods to the already written methods, while simply = will result in rewriting the list of method calls - Mark
 listOfHandlers += rnethodToCall; ... cl.RegisterWithCarEngine(OnCarEngineEvent); 
  • This fixes a bug, but does not explain anything. The author needs explanations. - tym32167 2:19 pm
  • Sometimes it seems to me that I speak Chinese or people do not purposely read the question to the end. And once again I repeat the question intentionally ignored: To directly directly specifically that line is interested in calling the RegisterWithCarEngine method and its argument which in fact is the call to the new method what exactly will happen here. Type creating a delegate object without a name and then we will pass it to the field that is in each object of the Car class and thus write down what method you need to call (I would understand if they passed the delegate variable, but I did it here and I want to clarify if I understood ) - Mark
  • What you have written will allow you to create a chain of method calls - Mark
  • @Mark most likely answered you this way because nobody is farther away from this line listOfHandlers = rnethodToCall; (which, by the way, is incorrect and will not compile) did not read, because, as I have already noted, it is uncomfortable to read poorly formatted code. - tym32167 2:46 pm
  • Apparently I’m too toxic a character and I find it difficult to control myself, but in the end I took the example from the book of Troelsen and yes I know that there are typos in the books (he ran into a couple reading Schild). Now I’ll go and try to collect this dumb guy in a heap and see if it starts up, but something tells me what it should because flipping through another metanit resource saw there is such a moment that delegates can be folded and done somehow like this del = del1 + del and the result of this action was that all methods from del1 and del2 were stored in del -