Introduction to Delegates
Introduction to Delegates
Fundamentals of Delegates
Introduction
As we saw from previous lessons, a function is a section of code that takes care of a job. The role of a function can be used or revealed by calling the function. Consider an example of a function as follows:
void Create() { Console.WriteLine("Actions speak louder than words."); }
To use such a function, you must explicitly call it. Here is an example:
static void Create() { Console.WriteLine("Actions speak louder than words."); } Create();
This would produce:
Actions speak louder than words Press any key to close this window . . .
In the strict sense and in the tradition of the C++ language, a function is created indipendently of any class so that the function can be called anytime, without going through a class. This concept of funcions is supported in the .NET library with the name of delegate and made available to C# (all other .NET languages, such as Visual Basic, F#, and other languages such as C++, PHP, Python, JavaScript, TypeScript, etc, support the concepts of functions).
Practical Learning: Introducing Delegates
Introduction to Creating a Delegate
So far in our lessons, we have studied various types that can be created from scratch. They are classes, records, structures, and enumerations. The delegate follows the same trend. This means that, like a class, a record, a structure, or an enumeration, a delegate can be created in its own file or in a file that also contains other types. To create a delegate in its own file, on the main menu, click Project -> Add New Item... Or, in the Solution Explorer, right-click the name of the project -> Add -> New Item... In the Add New Item dialog box, click Code File. Accept the default name of the file or change it. Click Add. In the document, type the necessary code. Remember that you can also create a delegate in a file that contains (a) class(es) and/or (an) enumeration(s).
The basic formula to create a delegate is:
[options] [modifier(s)] delegate void delegate-name ([formal-parameters]);
You can start with some opions. The modifiers can be one or an appropriate combination of the following keywords: new, public, private, protected, or internal. The delegate keyword is required. A delegate can be any of the types we have used so far. It can also be of type void. The delegate-name must be a valid C# name. The names of delegates follow the rules and suggestions of names of functions.
Because a delegate is some type of a template for a function or method, you must use parentheses, required for every function or method. If the function will not take any argument, leave the parentheses empty. Here is an example:
delegate string Observation();
A delegate is primarily a syntax for a function, but it is used to refer to an actual function. To use a delegate, you must define a function that would perform the actual job. That function must have the same return type and the same (number of) argument(s), if any. Here is an example:
delegate void Observation();
void Create()
{
}
Practical Learning: Creating a Delegate
delegate void Summarize();
Associating a Function to a Delegate
After creating the function, you can associate it to the name of the desired delegate. To do that, where you want to use the function, declare a variable of the type of the delegate. You have two options:
void Create() { } Quote qt = new Quote(Create); delegate void Quote();As seen in previous lessons, you can declare a variable using the var or the dynamic keyword. This is also valid for delegates. Here is an example:
void Create()
{
}
var qt = new Quote(Create);
delegate void Quote();
As seen in previous lessons for objects created from a class, a record, or a structure, if you declare a variable using the name of the delegate, after the new operator, you can omit the name of the delegate. This can be done as follows:
void Create()
{
}
Quote qt = new(Create);
delegate void Quote();
void Create()
{
return "A man with one watch knows what time it is; a man with two watches is never quite sure. - Lee Segall -";
}
Quote qt = Create;
delegate void Quote();
Practical Learning: Creating a Function for a Delegate
void Estimate()
{
}
delegate void Summarize();
Accessing a Delegate
Once you have associated a function to a delegate variable, you can use the delegate variable as if it were a defined function. That is, you can call it as you would proceed for a normal function. Here is an example:
Observation quote = new Observation(Create);
void Create()
{
Console.WriteLine("A man with one watch knows what time it is; " +
"a man with two watches is never quite sure. - Lee Segall -");
}
quote();
delegate void Observation();
This would produce:
A man with one watch knows what time it is; a man with two watches is never quite sure. - Lee Segall - Press any key to close this window . . .
Practical Learning: Accessing a Delegate
using static System.Console; void Estimate() { double taxes = 0.00; string residence = ""; WriteLine("Amazing DeltaX - State Income Tax"); WriteLine("==========================================="); WriteLine("States"); WriteLine("NN - None"); WriteLine("UT - Utah"); WriteLine("TX - Texas"); WriteLine("AK - Alaska"); WriteLine("NV - Nevada"); WriteLine("FL - Florida"); WriteLine("IN - Indiana"); WriteLine("WY - Wyoming"); WriteLine("IL - Illinois"); WriteLine("MI - Michigan"); WriteLine("CO - Colorado"); WriteLine("TN - Tennessee"); WriteLine("WA - Washington"); WriteLine("PA - Pennsylvania"); WriteLine("SD - South Dakota"); WriteLine("NH - New Hampshire"); WriteLine("NC - North Carolina"); WriteLine("-------------------------------------------"); Write("Type the 2-letter state: "); string state = ReadLine()!!; Write("Gross Salary: "); double salary = Convert.ToDouble(ReadLine()!!); switch (state.ToUpper()) { case null: taxes = 0.00; residence = "No Income Tax Collected"; break; case "AK": case "FL": case "NH": case "NV": case "SD": case "TN": case "TX": case "WA": case "WY": taxes = 0.00; residence = "Income Tax Not Collected"; break; case "PA": taxes = salary * 3.07 / 100.00; residence = "Pennsylvania (3.07%)"; break; case "IN": taxes = salary * 3.30 / 100.00; residence = "Indiana (3.3%)"; break; case "IL": taxes = salary * 3.75 / 100.00; residence = "Illinois (3.75%)"; break; case "MI": taxes = salary * 4.25 / 100.00; residence = "Michigan (4.25%)"; break; case "CO": taxes = salary * 4.63 / 100.00; residence = "Colorado (4.63%)"; break; case "UT": taxes = salary * 5 / 100.00; residence = "Utah (5%)"; break; case "NC": taxes = salary * 5.75 / 100.00; residence = "North Carolina (5.75%)"; break; default: taxes = 0.00; residence = "Default"; break; } WriteLine("Amazing DeltaX - State Income Tax"); WriteLine("==========================================="); WriteLine("Tax Preparation Summary"); WriteLine("-------------------------------------------"); WriteLine("Gross Salary: {0}", salary.ToString("F")); WriteLine("Employee Residence: {0}", residence); WriteLine("State Income Tax: {0}", taxes.ToString("F")); WriteLine("Net Pay: {0}", (salary - taxes).ToString("F")); WriteLine("-------------------------------------------"); WriteLine("==========================================="); } Summarize sum = new Summarize(Estimate); sum(); delegate void Summarize();
Amazing DeltaX - State Income Tax =========================================== States NN - None UT - Utah TX - Texas AK - Alaska NV - Nevada FL - Florida IN - Indiana WY - Wyoming IL - Illinois MI - Michigan CO - Colorado TN - Tennessee WA - Washington PA - Pennsylvania SD - South Dakota NH - New Hampshire NC - North Carolina ------------------------------------------- Type the 2-letter state: pa Gross Salary: 3066.48
Amazing DeltaX - State Income Tax =========================================== States NN - None UT - Utah TX - Texas AK - Alaska NV - Nevada FL - Florida IN - Indiana WY - Wyoming IL - Illinois MI - Michigan CO - Colorado TN - Tennessee WA - Washington PA - Pennsylvania SD - South Dakota NH - New Hampshire NC - North Carolina ------------------------------------------- Type the 2-letter state: pa Gross Salary: 3066.48 Amazing DeltaX - State Income Tax =========================================== Tax Preparation Summary ------------------------------------------- Gross Salary: 3066.48 Employee Residence: Pennsylvania (3.07%) State Income Tax: 94.14 Net Pay: 2972.34 ------------------------------------------- =========================================== Press any key to close this window . . .
Delegates and Parameters
Introduction
If you want to use a function that takes arguments and associate it to a delegate, when declaring the delegate, provide the necessary parameter(s) in its parentheses. Here is an example of a delegate that takes a parameter:
delegate void Simple(string arg);
When defining the associated method, make it take the same number and type(s) of parameter(s). Here is an example:
void Create(string msg)
{
Console.WriteLine(arg);
}
To associate the function to the delegate, declare a variable for the delegate and initialize it. You can use the new operator and the name of the delegate
void Create(string msg)
{
Console.WriteLine(msg);
}
Observation quote = new Observation(Create);
delegate void Observation(string arg);
To simplify your code, you can simply assign the name of the function to the delegate variable. Here is an example:
Observation quote = Create;
To actually use the delegate, when calling it, add the parentheses to it and in the parentheses, provide a value for the argument. Here is an example:
void Create(string msg) { Console.WriteLine(msg); } string str = "Chemistry is the study of matter. Matter is the building " + "block of anything in the environment, that is, anything " + "that occupies any form of space, anything that can be " + "seen or is hidden to the human eye."; Observation quote = new Observation(Create); // Accessing the delegate quote(str); delegate void Observation(string arg);
This would produce:
Chemistry is the study of matter. Matter is the building block of anything in the environment, that is, anything that occupies any form of space, anything that can be seen or is hidden to the human eye. Press any key to close this window . . .
Remember that you have various options to declare the variable. Here are examples:
void Create(string msg) { Console.WriteLine(msg); Console.WriteLine("-----------------------------------------------------------------------"); } // Regular variable declaration Observation quote1 = new Observation(Create); // Variable declared with the "var" or the "dynamic" keyword var quote2 = new Observation(Create); // Variable declared with the "new" operator only Observation quote3 = new(Create); string str = "Chemistry is the study of matter. Matter is the building " + "block of anything in the environment, that is, anything " + "that occupies any form of space, anything that can be " + "seen or is hidden to the human eye."; quote1(str); str = "A man with one watch knows what time it is; " + "a man with two watches is never quite sure. - Lee Segall -"; quote2(str); str = "Never let your head hang down. Never give up and sit down and grieve. " + "Find another way. And don't pray when it rains if you don't pray when " + "the sun shines. - Richard M Nixon"; quote3(str); delegate void Observation(string arg);
This would produce:
Chemistry is the study of matter. Matter is the building block of anything in the environment, that is, anything that occupies any form of space, anything that can be seen or is hidden to the human eye. ----------------------------------------------------------------------- A man with one watch knows what time it is; a man with two watches is never quite sure. - Lee Segall - ----------------------------------------------------------------------- Never let your head hang down. Never give up and sit down and grieve. Find another way. And don't pray when it rains if you don't pray when the sun shines. - Richard M Nixon ----------------------------------------------------------------------- Press any key to close this window . . .
In the above example, we used a string argument. In the same way, for the parameter, you can use any of the types we have used so far.
A Delegate with Many Parameters
A delegate can use more than one parameter. When creating the delegate, pass the same number and type(s) of parameter(s). Here is an example:
delegate void Operation(double x, string op, double y);
When calling the delegate from your variable, pass the appropriate number and type(s) of argument(s):
using static System.Console;
Write("Number 1: ");
double operand1 = double.Parse(ReadLine()!);
Write("Operator: ");
string oper = ReadLine()!;
Write("Number 2: ");
double operand2 = double.Parse(ReadLine()!);
Operation exam = delegate (double number1, string oper, double number2)
{
double result = 0;
switch (oper)
{
case "+":
result = number1 + number2;
break;
case "-":
result = number1 - number2;
break;
case "*":
result = number1 * number2;
break;
case "/":
if (number2 != 0)
result = number1 / number2;
break;
}
WriteLine("Result: " + result.ToString());
};
exam(operand1, oper, operand2);
delegate void Operation(double x, string op, double y);
Primary Characteristics of Delegates
A Static Function for a Delegate
You can associate a delegate to a static function. In this case, to access the function, you must qualify it from its class. Here is an example:
static void Create()
{
Console.WriteLine("A man with one watch knows what time it is; " +
"a man with two watches is never quite sure. - Lee Segall -");
}
Observation obs = new Observation(Create);
obs();
delegate void Observation();
Delegates and Namespaces
As mentioned previously, you can create a delegate in its own file. If necessary, to reduce name conflict, you can create a delegate in a namespace. In this case, to access the delegate outside of its namespace, you must qualify the delegate. To do this, type the name of the namespace, a period, and the name of the delegate. Here is an example:
void Create() { Console.WriteLine("A man with one watch knows what time it is; " + "a man with two watches is never quite sure. - Lee Segall -"); } Knowledge.Observation obs = new Knowledge.Observation(Create); obs(); namespace Knowledge { delegate void Observation(); }
Lambda Expressions
Introduction
We have already seen that the C# language supports the idea of nesting a function inside another function or inside a method. Here is an example:
Create();
// Optional other code here
void Create()
{
Console.WriteLine("A man with one watch knows what time it is; " +
"a man with two watches is never quite sure. - Lee Segall -");
}
A nested function can be associated with a delegate, exactly as we have done so far. After creating such a function, you can call it in the nesting code, before or after the code of the function. Here is an example:
Observation obs = new Observation(Create); obs(); void Create() { Console.WriteLine("A man with one watch knows what time it is; " + "a man with two watches is never quite sure. - Lee Segall -"); } delegate void Observation();
An Anonymous Delegate
In the previous examples, we had to create a function that would be associated with a delegate. In the same way, you can create a type of local implementation of a function (in an existing function). In other words, you don't have to define a formal function that would initialize the delegate. Such a function is referred to as anonymous.
Before implementing an anonymous function, first declare the delegate you will use, as we did previously. To create an anonymous function, declare a variable for the delegate and assign it the delegate keyword as if it were a function. That is, followed by parentheses and curly brackets that would represent the body of the function. In the body of the anonymous function or method, do whatever you want. Here is an example:
Observation obs = delegate ()
{
Console.WriteLine("A man with one watch knows what time it is; " +
"a man with two watches is never quite sure. - Lee Segall -");
};
delegate void Observation();
Once you have done this, you can then call the delegate variable as if it were a normal function. Here is an example:
Observation obs = delegate ()
{
Console.WriteLine("A man with one watch knows what time it is; " +
"a man with two watches is never quite sure. - Lee Segall -");
};
obs();
delegate void Observation();
The Lambda Operator
You can also create an anonymous function using an operator called lambda and represented by =>. From our example above, to use the lambda operator to create an anonymous method, omit the delegate keyword and follow the parentheses by the operator. Here is an example:
Observation obs = () =>
{
Console.WriteLine("A man with one watch knows what time it is; " +
"a man with two watches is never quite sure. - Lee Segall -");
};
obs();
delegate void Observation();
Parameterized Delegates and Lambda Expressions
You can create an anonymous method for a delegate that takes one or more parameters. You can do this using the delegate keyword. In its parentheses, pass an argument that is the same type as the argument of the delegate. Then, in the body of the method, you can use or ignore the parameter. When calling the variable of the delegate, use the same rules we have applied so far. Here is an example:
Observation obs = delegate(string x) { Console.WriteLine(x); }; string msg = "A man with one watch knows what time it is; " + "a man with two watches is never quite sure. - Lee Segall -"; obs(msg); delegate void Observation(string arg);
Instead of the delegate keyword, you can define an anonymous method using the lambda operator. In this case, in the parentheses of the lambda expression, enter the data type of the argument followed by its name. In the body of the anonymous method, use the argument or ignore it as you see fit. Here is an example:
Observation obs = (string x) =>
{
Console.WriteLine(x);
};
string msg = "A man with one watch knows what time it is; " +
"a man with two watches is never quite sure. - Lee Segall -";
obs(msg);
delegate void Observation(string arg);
In our example, we specified the type of the parameter. If you want, you can let the compiler figure out the type of argument. In this case, pass only the name of the parameter and not its type. Here is an example:
Observation obs = (x) =>
{
Console..WriteLine(x);
};
string msg = "A man with one watch knows what time it is; " +
"a man with two watches is never quite sure. - Lee Segall -";
obs(msg);
delegate void Observation(string arg);
In the same way, you can use a lambda expression to create an anonymous function that uses many parameters. You can implement it with the delegate keyword. Here is an example:
using static System.Console;
Write("Number 1: ");
double operand1 = double.Parse(ReadLine()!);
Write("Operator: ");
string oper = ReadLine()!;
Write("Number 2: ");
double operand2 = double.Parse(ReadLine()!);
Operation exam = delegate(double number1, string oper, double number2)
{
double result = 0;
switch (oper)
{
case "+":
result = number1 + number2;
break;
case "-":
result = number1 - number2;
break;
case "*":
result = number1 * number2;
break;
case "/":
if (number2 != 0)
result = number1 / number2;
break;
}
WriteLine("Result: " + result.ToString());
};
exam(operand1, oper, operand2);
delegate void Operation(double x, string op, double y);
You can omit using the delegate keyword and use the => operator instead. Here is an example:
Console.Write("Number 1: ");
double operand1 = double.Parse(Console.ReadLine()!);
Console.Write("Operator: ");
string oper = Console.ReadLine()!;
Console.Write("Number 2: ");
double operand2 = double.Parse(Console.ReadLine()!);
Operation exam = (double number1, string oper, double number2) =>
{
double result = 0;
switch (op)
{
case "+":
result = number1 + number2;
break;
case "-":
result = number1 - number2;
break;
case "*":
result = number1 * number2;
break;
case "/":
if (number2 != 0)
result = number1 / number2;
break;
}
Console.WriteLine("Result: " + result.ToString());
};
exam(operand1, oper, operand2);
delegate void Operation(double x, string op, double y);
Once again, the data types of the parameters are optional.
A Delegate as a Parameter
Passing a Function as Argument
By default, in C#, you cannot pass a function as argument (although some other languages support that concept). Delegates provide an alternative. Because a delegate is an object, or rather treated as such, it can be used as a parameter.
To start, you must have or create a delegate that has the syntax of the function you want to use. Here is an example:
delegate void Observation();
Once you have the delegate, you can create a function with the same syntax but that performs the action you want. Here is an example:
delegate void Observation();
void Hold()
{
}
After doing this, you can create a function to which you would pass the delegate as parameter. To do this, specify the name of the delegate as the data type of the parameter and add a name for the parameter. Here is an example:
void Hold()
{
}
void Create(Observation speech)
{
}
delegate void Observation();
In the body of the function, call the delegate as you would call a function. Here is an example:
void Create(Observation speech)
{
speech();
}
delegate void Observation();
When you call the function that takes a delegate as parameter, pass the name of the function that is associated to the delegate. This can be done in a class as follows:
Console.WriteLine("A man with one watch knows what time it is; a man with two watches is never quite sure. - Lee Segall -");
void Hold()
{
}
void Create(Observation speech)
{
speech();
}
Create(Hold);
delegate void Observation();
In the body of the function that takes a delegate as parameter, we simply called the parameter as a function. In some cases, you will want or need to initialize the argument. To do that, use the new operation to assign the delegate to the parameter as done when declaring a variable of a class. In the parentheses of the delegate, pass the name of the function that actually performs the desired operation. Here is an example:
void Hold()
{
}
void Create(Observation speech)
{
speech = new Observation(Hold);
speech();
}
Create(Hold);
delegate void Observation();
Remember that if you have a function that is called either once or by only another function, you can nest such a function in the body of the only function that needs its service. Here is an example:
void Hold()
{
Console.WriteLine("A man with one watch knows what time it is; a man " +
"with two watches is never quite sure. - Lee Segall -");
}
Create(Hold);
void Create(Observation speech)
{
speech();
}
delegate void Observation();
A Function as Parameter in a Lambda Expression
Instead of formally defining the desired behavior in the body of the function that would be associated with a delegate passed as parameter, you can create that function simply as an empty placeholder. Then use a lambda expression to define the behavior you want for the function. In this case, in the body of the function that takes the delegate as parameter, initialize the parameter with the delegate keyword and parentheses. Then create a curly-delimited body in which you can perform the operation(s) you want. Here is an example:
void Hold() { } void Create(Observation speech) { speech = delegate () { Console.WriteLine("A man with one watch knows what time it is; a man " + "with two watches is never quite sure. - Lee Segall -"); }; speech(); } Create(Hold); delegate void Observation();
Speaking of the delegate keyword, remember that you can omit it and use the => operator instead. Here is an example:
void Hold() { } void Create(Observation speech) { speech = () => { Console.WriteLine("A man with one watch knows what time it is; a man " + "with two watches is never quite sure. - Lee Segall -"); }; speech(); } Create(Hold); delegate void Observation();
Speaking of the function used as an empty placeholder, you can simply create in the section that needs it. This can be done as follows:
void Create(Observation speech) { speech = delegate() { Console.WriteLine("A man with one watch knows what time it is; a man " + "with two watches is never quite sure. - Lee Segall -"); }; speech(); } Create(Hold); void Hold() {} delegate void Observation();
Or like this:
void Create(Observation speech) { speech = () => { Console.WriteLine("A man with one watch knows what time it is; a man " + "with two watches is never quite sure. - Lee Segall -"); }; speech(); } Create(Hold); void Hold() {} delegate void Observation();
A Delegate with a Parameter
We have already seen that you can create a delegate that takes a parameter. Here is an example of a delegate that takes a string as parameter:
delegate void Observation(string x);
Such a delegate can be passed as parameter to a function. To start, create a function that uses the same syntax as the delegate and that performs the action you want. Here is an example:
void Hold(string msg) { Console.WriteLine(msg, "Citations"); } delegate void Observation(string x);
Create a function that takes the delegate as patameter. In the body of the function, call the parameter as a function but pass an argument that is the same type the delegate takes. Then, when calling the function that takes the delegate as parameter, pass the name of the delegate-associated function as argument. Here is an example:
using static System.Console; void Hold(string msg) { WriteLine(msg); } void Create(Observation speech) { speech("A man with one watch knows what time it is; a man " + "with two watches is never quite sure. - Lee Segall -"); } Create(Hold); delegate void Observation(string x);
A Delegate with Parameters
You can create a delegate that takes more than one parameter. Follow the above steps to pass the delegate as argument.
A Method as Parameter in a Lambda Expression
Instead of creating a formal method that would be associated with a delegate passed as parameter, you can define a lambda expression in the place where the delegate variable is passed as argument.
Delegates Compositions
Adding Delegates
One of the characteristics that set delegates apart from C/C++ function pointers is that a delegate can be added to another using the + operator. This is referred to as composition. This is done by adding one delegate variable to another as in "result = a + b". After doing this, to access the action produced, call the variable as if it were a function, as in "result()". In the same way, you can add many delegates as in "result = a + b + c". or more. You can call the variable like a function as in "result()".
Delegates and Compound Addition
Besides the regular addition, delegates also support the compound addition performed using the += operator. It works exactly as in C# arithmetic. This means that you can first add a delegate to a variable and assign the result to the variable as follows:
result = first;
result = result + second;
As an alternative, you can use the += operator between the variable and the right operand.
Introduction to Delegates and Classes
Introduction to Delegates and Methods
Everything we learn about a function is also applicable to a method as long as we keep in mind that a method is a member of a class.
A delegate can be associated with a method of a class. If the delegate is created outside the class, the method must be accessible; that is, the method should be created as public or internal. This time, to access the method and associate it to a delegate, you have to first declare a variable of that class. Here is an example:
Sociology soc = new Sociology(); Observation obs = new Observation(soc.Create); obs(); delegate void Observation(); public class Sociology { public void Create() { Console.WriteLine("A man with one watch knows what time it is; " + "a man with two watches is never quite sure. - Lee Segall -", "Citations"); } }
Delegates and Classes
So far, we involved only primitive types with delegates, especially when it came to parameters. Indeed, any type of value can be passed to a delegate and subsequently to its associated method(s). You can use any appropriate class. For example you can create your own class, but as you know it already, the .NET library provides a rich library of classes you can use.
Practical Learning: Ending the Lesson
|
|||
Previous | Copyright © 2008-2023, FunctionX | Wednesday 20 October 2021 | Next |
|