Download as docx, pdf, or txt
Download as docx, pdf, or txt
You are on page 1of 13

CONTENTS OF THE SUBJECT 

 Hide 
1. What is Delegate? Why is a delegate needed in C#?
1.1. Role of delegate in C#
1.2. Illustration
2. Programming techniques with delegates in C#
2.1. Declaring delegate type
2.2. Description of methods and delegates
2.3. Declare and use delegate variable
2.4. Pass a delegate type parameter to the method
3. Generic delegate
3.1. Actions
3.2. Funcs
3.3. Predicate
4. Using delegates with anonymous methods, lambda functions, local functions
4.1. Anonymous method
4.2. lambda . function
4.3. Local function
5. Conclude

What is Delegate? Why do I have a delegate in C #?


To understand this data type, let's look at some of the following scenarios.

Suppose we need to build a class, in this class will have to call a method to
perform a certain action. However, we do not know this method when building
the class! This method only appears when someone else uses the class to
instantiate the object.

Have you ever touched windows forms or wpf ? If you are sure you will notice,
the button (Button) is a built-in class, and the button event handler method
(OnClick) is written by you. How does the constructor of the Button class know
and run the event handler method you wrote?

So how to fulfill the requirement of “ calling a method when the method


does not exist or is undefined ” at the class construction stage?

Situations where it is not known in advance which specific method to call leads
to the use of a special kind of tool in C#: delegate.

Delegates are data types in C# whose variables contain references to


methods (methods) , instead of containing values or containing references to
objects of normal classes.

1
The term "delegate" translated into Vietnamese can be either a representative type
or a proxy type . However, these two translations are not commonly used in the
literature. So, in this article we will use the original English term – delegate .

A variable created from a delegate type is called a delegate variable  functons


or methods .

Each delegate type, when defined, only allows its variable to contain references
to methods that conform to this delegate's rules.

The role of a delegate in C#


Delegate allows a class to be more flexible and flexible in using methods.
Accordingly, the specific content of a method is not defined in the class but will
be defined by the user of that class during object initialization. This helps to
divide the logic of a class into different parts and built by different people.

Delegates are used to help a class object interact back with the entity that
creates and uses that class. This keeps the class from being "isolated" within
that entity. For example, delegates help to call the methods of the actual
creation and containment of the class object.

With the ability to create such backward interactions, delegates become the
core of the event-driven programming model, used in winforms and WPF
technologies.

For example, when building windows form controls (buttons, select buttons,
menus, etc.), the programmer of these classes cannot define what the user
wants to do when the button is clicked, when the menu is selected. . Therefore,
it is imperative to use the mechanism of delegates to pass this logic on to users
of those classes to write code. Thus, the Button when created in a Form has the
ability to interact with the Form's code, not isolate itself.

Delegates are also commonly used with the asynchronous programming model
in the form of callback methods, or in multithreaded programming.

An example to illustrate this


Let's implement and analyze the following example to better see how to declare
and use delegates in C#.

In this example, we "simulate" a program that helps calculate and graph


functions (just a dummy drawing, not a real one). In it we will write a class that

2
calculates and prints the value of a function in a range of values. The actual
function will be created by the user of the class and provided later.

Note:

Delegate: MathFunction (double x)

Delegate variable: Funtion(double x)

Class: Graph

using System;
namespace ConsoleApp
{
/* declare the MathFunction delegate type:
* this type is described as (double) -> double
* means that any function can be assigned "taking a variable of type double,
* returns type double" for a variable of type MathFunction
*/
internal delegate double MathFunction ( double x ) ;
// simulate the graphing of functions
internal class Graph
{
/* declares a property of type MathFunction.
* MathFunction is used as normal data types
*/
public MathFunction Function { get ; set ; }
/* This method takes an input parameter of type delegate MathFunction.
* The delegate type as a parameter is no different from the normal data type
*/
public void Render ( MathFunction function, double [] range )
{
// can assign variables of type delegate as usual
Function = function;
// since function is a normal object, it also has properties
// and methods like other objects. Practically all delegate types are
// inherit the System.Delegate class. Here we are using the Method . attribute
// of this class.
Console. WriteLine ( $ "Drawing the function graph: {function.Method} " ) ;
foreach ( var x in range )
{
// even though a function is an object, it can be "called" like a function call.

3
// this is the difference between object of type delegate and object
//create from normal class
var y = function ( x ) ;
// In addition to this call, you can also use the following structure
// var y = function.Invoke(x);
//
// var y = function?.Invoke(x);
Console. Write ( $ " {y:f3} " ) ;
}
Console. WriteLine ( "rn-----------------" ) ;
}
}
// a test class containing methods with description (double)->double
internal class Mathematics
{
// here is an instance method
public double Cos ( double x ) = > Math. Cos ( x ) ;
// this is a static method
public static double Tan ( double x ) = > Math. Tan ( x ) ;
}
internal class Program
{
// another static method
private static double Sin ( double x )
{
return Math. Sin ( x ) ;
}
private static void Main ( string [] args )
{
Graph graph = new Graph () ;
// initialize the value range of x
double [] range = new double [] { 1.0 , 1.2 , 1.3 , 1.4 , 1.5 , 1.6 , 1.7 , 1.8 , 1.9 , 2.0 } ;
// pass Sin function as parameter to Render
graph. Render ( Sin, range ) ;
// pass static Tan function to Render
graph. Render ( Mathematics Tan , range ) ;
// pass the Cos instance function to Render
Mathematics math = new Mathematics () ;
graph. Render ( math. Cos , range ) ;
// pass a built-in function Sqrt of the Math class in .net
graph. Render ( Math. Sqrt , range ) ;
// create an anonymous function that conforms to the description (double) -> double

4
// and assign it to the function variable
// variable function is a variable of type delegate MathFunction
MathFunction function = delegate ( double x ) { return x *= 2 ; } ;
// pass function variable to Render function
graph. Render ( function, range ) ;
// declare and pass anonymous function directly at parameter position
graph. Render ( delegator ( double x ) { return x++; } , range ) ;
// declare and pass lambda function directly at parameter position
graph. Render (( double x ) = > { return x *= 10 ; } , range ) ;
// pass a shortened lambda function as parameter
graph. Render ( x = > x / 10 , range ) ;
Console. ReadKey () ;
}
}
}

Delegate programming techniques in C # _ _ _ _ _


In this section we will analyze the technical details of using delegates
encountered in the above illustration.

Declare the delegate type


In the above example, we declare a delegate type named MathFunction directly
in the namespace.

syntax

internal delegate void MathFunction() ;


Since the delegate type has the same level as the class, it can be declared
directly in the namespace, as well as can be declared as an internal type inside
the class (just like declaring a class inside a class).

type of MathFunction is similar to a method that takes a variable of type


double and returns a double value .

Here we see, formally, declaring a delegate type is the same as declaring a


method without a body, just adding the keyword delegate before the return
type and ending the declaration with a semicolon.

5
Description of cv and delegate methods _ _ _
To make it easier to describe the method (and delegate type), a descriptive
convention is given as follows:

( parameter list by type ) -> output type.

private float method Div(int a, int b){}

whose description is ( int, int ) -> float.

When reading this description we will understand: the method (regardless of the
name) takes two input parameters of the same type int and returns a result of
type float.

With this writing convention, WITH the delegate MathFunction above


represents all the measures described as ( double ) -> double .

All methods with description (double)->double are assignable to variables of


type MathFunction . This description is also called the description of
MathFunction .

Declare and use the delegate variable _


After declaring the delegate type , you can declare variables of this data type
similar to declaring normal variables:

/* declares a property of type MathFunction.


* MathFunction is used as normal data types
*/
public MathFunction Function { get ; set ; }

Delegate parameters can be declared and assigned as normal parameters:

/* This method takes an input parameter of type delegate MathFunction.


* The delegate type as a parameter is no different from the normal data type
*/
public void Render ( MathFunction function, double [] range )
{
// can assign variables of type delegate as usual
Function = function;

6
A variable of type delegate is also an object, similar to objects created from a
class, it also has properties and methods like normal objects. In fact, all user-
defined delegate types inherit from the System.Delegate class , so they also
inherit the properties and methods of this class.

Because the delegate variable will contain a reference to a method, we can use
the delegate variable name like a real method, meaning we can "call" this
variable like calling a normal method. When "calling" a delegate variable, the
method it points to will be executed.

// since function is a normal object, it also has properties


// and methods like other objects. Practically all delegate types are
// inherit the System.Delegate class. Here I am using the Method . attribute
// of this class.
Console. WriteLine ( $ "Drawing the function graph: {function.Method} " ) ;
foreach ( var x in range )
{
// even though a function is an object, it can be "called" like a function call.
// this is the difference between object of type delegate and object
//create from normal class
var y = function ( x ) ;
// In addition to this call, you can also use the following structure
// var y = function.Invoke(x);
// check the delegate variable before calling to avoid errors
// var y = function?.Invoke(x);
Console. Write ( $ " {y:f3} " ) ;
}

In addition to "calling" the delegate variable like calling a method, the delegate
types also have an Invoke method that calls the method this variable points to:

var y = function.Invoke(x);
More carefully we can check the object before calling Invoke:

var y = function?.Invoke(x);
Maths? Allows you to check if a function object has a null value. If the object has a non-null
value, the Invoke method is executed. This usage is the safest. If the function receives null
value, the Invoke method will not be called. Without checking for null, a method call on a
null object will raise an error.

Pass the delegate type parameter to the method _ _ _


At the object initialization stage of the class, the user of the new class passes a
specific method for a parameter of type delegate.

7
// pass Sin function as parameter to Render
graph. Render ( Sin, range ) ; // public void Render(MathFunction function, double[] range)

// pass static Tan function to Render


graph. Render ( Mathematics Tan , range ) ;
// pass the Cos instance function to Render
Mathematics math = new Mathematics () ;
graph. Render ( math. Cos , range ) ;
// pass a built-in function Sqrt of the Math class in .net
graph. Render ( Math. Sqrt , range ) ;

If a method parameter is a delegate variable, we can directly pass the name of


a method that shares the same description as the delegate type.

Note: passing a method as a parameter is different from passing a method call as a


parameter. Passing method calls can be considered equivalent to normal data passing
(string, bool, int, etc.), no delegate involved.

Generic delegate
As we know when looking at delegates, to work with delegates we need to first
declare the delegate as a normal data type declaration, and then use that
delegate type to declare variables. At the use stage we assign that delegate
variable with a method that matches the requirements of the delegate type.

C# helps programmers by defining a series of generic delegate data types that


we can directly use to declare variables. Using a generic delegate eliminates the
stage of declaring the delegate type.

Read this article to better understand generics in C# .

Basically, generic delegates are predefined delegate types using generic


mechanism. The .NET framework defines three groups of generic delegates:
Actions, Funcs, and Predicates.

Actions
Actions are delegate types corresponding to methods that return no data
(output is void). Action types are defined in the System namespace as follows:

namespace System
{
public delegate void Action () ;

8
public delegate void Action < in T >( T obj ) ;
public delegate void Action < in T1, in T2 >( T1 arg1, T2 arg2 ) ;
// there are other similar delegates
// .NET framework defines a total of 16 such delegates with the number of input parameters from 1 to 16.
}

The .NET framework defines a total of 16 generic delegates, the first one has 1
input parameter, the last one has 16 input parameters.

Thus, the type Action<T1,..> can correspond to any method that returns no
value and has between 1 and 16 input parameters (any type). Only delegate
void Action() corresponds to methods that take no parameters and return no
value.

Thus, when using Action or Action<T1,..>, there is no need to declare delegate


types with output type void anymore (and have less than 16 input parameters).

Here are some examples of how to use action types:

Action action1 = () = > Console. WriteLine ( "Hello world" ) ;


Action < string > action2 = ( s ) = > Console. WriteLine ( s ) ;
Action < string , int > action3 = ( s, i ) = > { for ( int j = 0 ; j < i; j++ ) Console. WriteLine ( s ) ; } ;

Funcs
Funcs are delegate types that correspond to methods that return data. The
funcs types are defined in the System namespace as follows:

namespace System
{
public delegate TResult Func < out TResult >() ;
public delegate TResult Func < in T, out TResult >( T arg ) ;
public delegate TResult Func < in T1, in T2, out TResult >( T1 arg1, T2 arg2 ) ;
// there are 17 similar delegates
}

Similar to actions, the .NET framework also defines 17 delegate types as above
with the number of input parameters from 0 to 16.

Funcs type definitions differ from actions in that funcs must always have an
output type at the end of the list of pseudo-generic types (out TResult). Here
are some examples using funcs:

9
Func < int > func1 = () = > 0 ;
Func < int , int > func2 = ( i ) = > i * 10 ;
Func < int , int , float > func3 = ( a, b ) = > a / b;
Console. WriteLine ( $ "func1: {func1} ; func2: {func2(10)} ; func3: {func3(1, 2)} " ) ;

The variable func1 is declared and refers to a lambda function that takes no
parameters and always returns 0 (of type int); variable func2 refers to a lambda
function that takes an int parameter, multiplies that parameter by 10 and
returns this result (of type int); variable func3 refers to a lambda function that
takes two integers and returns the division result (type float).

Predicate
Predicate is a predefined delegate type as follows (in System):

namespace System
{
public delegate bool Predicate <[ NullableAttribute ( 2 )] print T >( T obj ) ;
}

Thus, predicate is a delegate type corresponding to methods that take 1 input


parameter and return a bool value. Predicate is used in comparison expressions.
Here are some examples:

Predicate < int > predicate1 = ( i ) = > i > 100 ;


Console. WriteLine ( $ "is 10 > 100? it's {predicate1(10)} " ) ;
Predicate < string > predicate2 = ( s ) = > s. length > 10 ;

The variable predicate1 refers to a lambda function that compares whether an


integer is greater than 100; variable predicate2 refers to a lambda function that
takes a string of characters and compares if the string length is greater than 10.

As we see in the examples above, we all use lambda functions with delegates.
When using this style we can completely omit the type declaration of the
parameter in the lambda function because C# can infer the type by itself. In
addition, if the method body has only one statement, use the "expression body"
writing style for brevity.

Using delegates with anonymous methods , lambdas


, and tools _ _ _ _ _ _ _ _ _ _
To pass a parameter of the delegate type to a method, we must write the
methods with the description required by the delegate type.

10
Due to the specificity of C#, methods must be defined in a class (whether
instance method or static method). For instance methods, we also have to
initialize the object before passing this method as a parameter to another
method via delegate.

If the method is only used once as a parameter, or the method is too simple
(consisting of only one line of code), building such small batches of methods can
cause code fragmentation. making it difficult to track and manage.

C# 2 introduced the concept of anonymous methods , C# 3 introduced the


concept of a lambda function (lambda statement) to help build such single-use
methods. The lambda function is more commonly used nowadays.

Anonymous cv method _ _ _ _
Anonymous methods are also a type of method, but differ from normal methods
in a number of ways:

1. No name: since anonymous methods are mainly used as parameters to


other methods, the most important thing is the operation of the
method (method body), and the name is not important; Methods of
this type are also not called (re-used) in many places in the code like
normal methods, so no name is needed.
2. Can be declared directly where it is needed: for example, it is possible
to declare it directly in the parameter list of another function, and thus,
can also access local variables of the method where it is declared.
In the docs two can be used: anonymous method or anonymous method .
These two terms are equivalent. In this lecture we unanimously call
anonymous methods .

Let's see the following example of anonymous method declaration:

We continue to use the illustrative example we did in the previous post. Here
we do not declare more normal methods as before, but directly declare an
anonymous method and assign it to a function variable of type MathFunction :

// create an anonymous function that conforms to the description (double) -> double
// and assign it to the function variable
// variable function is a variable of type delegate MathFunction
MathFunction function = delegate ( double x ) { return x *= 2 ; } ;
// pass function variable to Render function
graph. Render ( function, range ) ;
// declare and pass anonymous function directly at parameter position

11
graph. Render ( delegator ( double x ) { return x++; } , range ) ;

We can also declare anonymous methods directly in the parameter position:

graph.Render(delegate (double x) { return x++; }, range);


Through this example, it can be seen that the anonymous method declaration is
only different from the normal method in that the method name position is
replaced by the delegate keyword.

lambda . function
Lambda functions also have the characteristics of anonymous methods, but can
omit the type declaration of the parameters.

This comes from the fact that when a lambda function is declared as a
parameter of a method, its parameters must conform to the delegate's
description. Therefore, the C# compiler can infer the type of the parameters on
its own without having to write it out explicitly.

Similarly, a lambda function is declared exactly like a normal method, the


biggest difference is that it can be declared directly in another method body
(like an anonymous method), and the parameter list is Connect to the method
body with a => sign. The lambda method is also very often used with the
expression body.

// declare and pass lambda function directly at parameter position


graph. Render (( double x ) = > { return x *= 10 ; } , range ) ;
// pass a shortened lambda function as parameter
graph. Render ( x = > x / 10 , range ) ;

In the second scenario we see that the parameter list of the lambda function
doesn't even have a data type name.

The reason is because this function is used as the first parameter to the Render
method. This parameter has been specified as (double) -> double. Therefore,
the parameter x will be interpreted by C# as being of type double. This type
inference, combined with the expression body, makes lambda functions very
succinct.

Let's see the execution result of the above example program:

12
Program execution results
Where we pass anonymous methods and lambda functions, the C# compiler
generates names for these functions on its own. However, since the code
doesn't have a name, we don't have the ability to call these functions again in
other places in the code.

cb tool function _
From C# 7, we have one more possibility to construct a method within another
method body besides using anonymous methods and lambda functions: using
local functions .

A local function is a method defined inside another method's body.

Local functions are not at all different from regular methods. However, the local
function does not have the access control keyword and can only be called within
the method body that contains it.

If you don't know: in C# there are two things that are different from many other languages
of the C/C++ family: (1) can declare classes inside classes, and (2) can declare methods
inside methods.

13

You might also like