Reputation:
I'm new to C#, I'm very confused with the terms used like 'listener', 'caller', 'callback', 'EventHandler', let's say we have the following code below:
public class Car
{
public delegate void CarEngineHandler();
public CarEngineHandler listOfHandlers;
public void Accelerate(int delta)
{
if (delta > 80)
listOfHandlers(); // not checking null for simplicity
}
}
class Program
{
static void Main(string[] args)
{
Car myCar = new Car();
myCar.listOfHandlers = new Car.CarEngineHandler(CallWhenExploded);
myCar.Accelerate(120);
}
static void CallWhenExploded()
{
Console.WriteLine("Sorry, this car is dead...");
}
}
so in this scenario:
Who is the caller?
Who is the listener? listen to what?
Who is the callback?
Who is the EventHandler?
and my answer is:
CallWhenExploded()
static functionam I correct? I really don't know who is the listener and listen to what.
Upvotes: 0
Views: 81
Reputation: 74660
Hopefully you understand that an variable of type string is like a bucket that holds a string of characters, and you can look at them individually, pass them round as a collection etc. It's quite an easy thing to visualize, this variable being a container that stores data, because we store data all the time in the real world; pieces of paper in a box/file etc
Delegates are like variables for methods - and this is a much harder concept to understand, that we can pass a method round as though it were just like data, and we can get the computer to run the method.. but that's what a delegate is. When you declare a delegate, it's like when you declare your own class:
delegate void SomeMethod(); //specify a delegate
class Person{ //specify a class
string FirstName;
string LastName;
}
Just like when you make a class, you're giving yourself the ability to create an instance of that class, when you declare a delegate you're giving yourself the ability to create an instance of that delegate. Your class instance refers to data, like here the first and last name of the person. A delegate instance refers to a method rather than referring to data. You can make one class instance that refers to "John" and "Smith". You can make a delegate instance that refers to Method1(). You can make another class instance that refers to "Bill" "Jones". You can make another delegate instance that refers to Method2().
You can associate any method with your delegate instance, so long as the method has a signature that is equal to the delegate. That delegate above will accept a reference to any method that takes no parameters and returns void. If you've got one or more of these kind of methods you can attach them to an instance of the delegate, pass the delegate instance like a variable, and the thing you've passed the delegate to can call the methods without needing to know what the method name is or anything about it.
Why would we want to do this? One major use of delegates is in event handling. Microsoft created UI controls that are capable of doing things - buttons you can click on. You'll want your button to do something when it's clicked, but everyone in the world will want their button to do something different when it's clicked, so Microsoft can't write the code for that. They can however say that they will make a button that takes a delegate (a method passed like a variable) and the button promises to call the method when it is clicked. You supply the code that does what you want, as a delegate, to the button. Your method has to have a certain signature (arguments) because the button wants to pass your method some data that may be helpful to you; perhaps the location of the mouse when the clock occurred, or the number of times it was clicked. UI components have hundreds of different kinds of events, they all run on delegates
Another good example of delegates is in the old style of async programming when we had Begin/End pairs of methods, calling Begin would typically start a new thread off doing something and we would have to supply a method (our method) to it that, when Microsoft's Begin method had finished doing something it would call our method to let us know it was done. Typically then our code would call End, passing in some reference our method had been given, and we'd get the result. Sending a request to a server would take some time, for example, so we'd Begin it, go do some other things, and when the response was ready we would be notified by our delegate being called and we would process the result
So, get used to the notion that a delegate is just a way or referring to a method, you pass it around, what you pass it to can run it and provide data to it without having a clue what it does. In terminology it is also referred to as a callback - the thing that will be called when the foreign method is done with its work, though it's a fairly loose term - it might be used to refer to either your method that does the work, or the instance of a delegate pointing to your method, the thing that is passed in. Effectively they're equivalent dependi on the context of the conversation
A delegate itself is more like a collection/array - you can attach multiple methods to it and when the delegate is called they will all run, though not in any defined order. In your example you assigned just one to it but it could be multiple:
SomeMethod delegs = null;
delegs += new SomeMethod(MyMethod1);
delegs += new SomeMethod(MyMethod2);
Invoking that delegate will cause both MyMethod1 and MyMethod2 to be run, possibly in 2,1 order
Events are delegates too, but they're a slightly special case, touched on above with the button click handler. When a delegate is specified with the event keyword it can only be invoked inside the class that declared the delegate variable. UI controls use this to ensure that only they can run the delegate; you can't reach into a button and force run its clock handlers yourself because the delegate list that handles a click is declared as an event. If you want to programmatically cause a button to fire its event handler list you normally have to subclass it so your code becomes operationally "inside" the button code
Listener as a phrase is more often associated with events but it's reasonably synonymous with callback and eventhandler - when you add a listener to an event, you're saying that you're writing code that will handle the occurrence of the event, your code (as a delegate/event handler) that acts when the event occurs. It doesn't "listen" per se; it just hangs around and gets kicked into action when the thing raising the event invokes the list of delegates/eventhandlers associated with the event.
As an aside, because delegate instances represent a variable that points to a method, i.e. they point to an action, your naming of the instances should be a verb, not a noun. You called yours listOfHandlers but it would have been more helpful to your understanding to call the delegate instance after the event/action that is occurring. The use case you were applying was one of overrevving so perhaps you should name it CarEngineOverRevving. You could have another for when the temperature exceeds. If both these delegates has the same signature (you want people who will handle these events to do so via a method with no arguments) then you can reuse the same delegate for the different events:
public void delegate CarEngineProblemHandler();
public CarEngineProblemHandler CarEngineOverRevving = null;
public CarEngineProblemHandler CarEngineOverheating = null;
The delegate is your way of declaring what you want the methods provided to you, that you will call, to look like. The list of named problems is what you are declaring other people can provide some handling for. I could decide to not give you a handler for overrevving because I will never do it, but overheating might happen anyway so I'll certainly give you a handler/listener/delegate/callback to call of it ever happens
Upvotes: 2
Reputation: 111
You should be able to answer those questions when you understand 'broadcaster/subscriber' pattern used by delegates. 'broadcaster' holds the delegate instance and decides when to call target methods in its 'invocation list'. These target methods are the 'subscribers' and they can decide when to stop or start 'listening' by calling '-=' or '+=' on broadcaster's delegate field.
public class Car
{
// This is the 'delegate type'
// ‘Delegate type’ is what is responsible for defining the method such as its signature
// which include return type and parameters
// In this ‘Delegate type’ named:'CarEngineHandler', the return type is 'void' and there are no parameters
public delegate void CarEngineHandler();
//this is the ‘Delegate instance’
//‘Delegate instance’ is responsible for holding the reference to the actual methods
//that adheres to the delegate type.
//Delegate instance can be used to then ‘invoke’ that method whenever needed.
public CarEngineHandler listOfHandlers;
public void Accelerate(int delta)
{
if (delta > 80)
//this is where you use the 'Delegate instance' to ‘invoke’ the methods
// You can also you use: listOfHandlers.Invoke();
// which is same as calling 'listOfHandlers();'
// When delegate instance is invoked, all its actions in its invocation list are executed in order
//In this example it will first dispaly: "Sorry, this car is dead..."
// then display: "Calling, emergency..."
listOfHandlers(); // not checking null for simplicity
}
}
class Program
{
static void Main(string[] args)
{
Car myCar = new Car();
//This is where you add to the 'delegate instance', the 'target method's' that matches delegate signature
//A 'delegate instance' can contain more than one action. This list of actions is called the “invocation list”.
//You can also add multiple methods to invocation list like this: myCar.listOfHandlers += CallWhenExploded;
myCar.listOfHandlers = new Car.CarEngineHandler(CallWhenExploded);
// For example you can add another target method like this:
myCar.listOfHandlers += CallEmergencyService;
// you get details about the 'target methods' attached to the “invocation list” like this:
var listOfTargets = myCar.listOfHandlers.GetInvocationList();
Console.WriteLine(listOfTargets[0].Method.Name);
myCar.Accelerate(120);
}
static void CallWhenExploded()
{
Console.WriteLine("Sorry, this car is dead...");
}
static void CallEmergencyService()
{
Console.WriteLine("Calling, emergency...");
}
}
Upvotes: 0