C# delegates, generic delegates, and Lambda expressions

catalogue

 

#What is delegation

1. In terms of data structure, delegate is a user-defined type like class.
2. A delegate is an abstraction of a method. It stores the addresses of a series of methods with the same parameters and return types. When a delegate is called, all the methods contained in the delegate will be executed.

#Delegate declaration, instantiation, and invocation

Back to the top

1. Statement

Delegate is a special class, so the declaration method of delegate is similar to that of class. Delegate can be declared wherever class can be declared. The delegate declaration uses the delegate keyword. At the same time, the delegate should specify the method parameters and return values. The writing method is similar to that of the method. The declaration of the comprehensive class and the declaration of the method. The delegate declaration is written in the following form:

[access modifier] delegate return value type delegate name (formal parameter list);

public delegate void MyDel();//Defines a delegate MyDel, which can register functions that return void type and have no parameters
public delegate void MyDel1(string str);//Defines a delegate MyDel1, which can register a function that returns void type and has a string as a parameter
public delegate int MyDel2(int a,int b);//Defines a delegate MyDel2, which can register functions that return int types and have two ints as parameters

Back to the top

2. Instantiation of delegate

The use method is the same as that of ordinary classes. After declaring the delegate, we must pass a specific method to the delegate to call the delegate instance at run time. The delegate instance contains the information of the method passed to it. At run time, calling the delegate instance is equivalent to executing the method in it.

The format of delegate instantiation is as follows:

Delegate class name delegate instance name = new delegate class name (Target);

Where, the delegate instance name is the user-defined name, and the Target is the name of the method to be passed in. Note that Target is a reference to a method and cannot be marked with (). With (), this method is called. Distinguish between references and calls.
There is another simple way to instantiate a delegate:

Delegate class name delegate instance name = Target;

You can directly pass in the Target reference where the delegate instance is needed, and the C# compiler will automatically verify according to the delegate type, which is called "delegate inference".

MyDel2 testDel=new MyDel2(Add);
MyDel2 testDel1 = Add;

Back to the top

3. Call of delegate instance

A delegate instance is equivalent to the actual method in it. Therefore, you can call the delegate instance with the reflected Invoke() method or directly add () to the delegate instance.

int num = testDel(1,2);
int num1 = testDel.Invoke(1, 2);

Back to the top

4. Delegate complete simple example

namespace delegateTest
{
    public delegate int MyCalculator(int num1, int num2);
    class Program
    {
        static void Main(string[] args)
        {
            MyCalculator myCal=new MyCalculator(Add);
            int addNum= myCal(1,2);

            MyCalculator myCal1 = Sub;
            int subNum = myCal1.Invoke(1, 2);

            Console.WriteLine("addNum:{0},subNum:{1}", addNum, subNum);
            
            int calNum = Calculate(1, 2, Add);
            Console.WriteLine("calNum:{0}", calNum);
        }

        static int Add(int num1, int num2)
        {
            Console.WriteLine("num1 + num2={0}",num1 + num2);
            return num1 + num2;
        }
        static int Sub(int num1, int num2)
        {
            Console.WriteLine("num1 - num2={0}", num1 - num2);
            return num1 - num2;
        }
        static int Calculate(int num1,int num2,MyCalculator calDel)
        {
            return calDel(num1,num2);
        }
    }
}

Console print results:

num1 + num2=3
num1 - num2=-1
addNum:3,subNum:-1
num1 + num2=3
calNum:3

#Generic delegate

Every time we want to use a delegate, we need to declare the delegate class, specify the parameters and return value types, and then instantiate and call it. To simplify the process NET framework encapsulates three generic delegate classes for us, so in most cases, we don't have to declare delegates, and we can instantiate them directly, which is convenient for our daily code writing.
The three generic delegates include Func delegate, Action delegate and Predicate delegate.

Back to the top

1. Func entrustment

Func delegates represent generic delegates that have return values. Func has a series of overloads, such as func < T1, T2 TResult >, where TResult represents the return value type of the delegate, and the rest are parameter types. When there is only one T, func, it means that the delegate is parameterless NET encapsulates function < > delegates with up to 16 input parameters.
It should be noted that if the method does not return a value, that is, it returns void. Since void is not a data type, Func delegates cannot be defined. For the generic delegate that returns void, see Action below.
Func is used in the same way as a general delegate. For example, the above case can be rewritten as follows:

namespace delegateTest
{
    class Program
    {
        static void Main(string[] args)
        {
            int calNum = Calculate(1, 2, Sub);
            Console.WriteLine("calNum:{0}", calNum);// -1
        }
        static int Calculate(int num1, int num2, Func<int, int, int> calDel)
        {
            return calDel(num1,num2);
        }
        static int Sub(int num1, int num2)
        {
            Console.WriteLine("num1 - num2={0}", num1 - num2);
            return num1 - num2;
        }
    }
}

Back to the top

2. Action delegation

The Action delegate represents the delegate whose return value is null void. It also has some column overloads and has up to 16 input parameters. The usage is the same as Func.

namespace delegateTest
{
    class Program
    {
        static void Main(string[] args)
        {
			DoSome("hello",Say);// hello
        }
        static void DoSome(string str,Action<string> doAction)
        {
            doAction(str);
        }
        static void Say(string str)
        {
            Console.WriteLine(str);
        }
    }
}

Back to the top

3. Predicate delegate

This is generally used less. It encapsulates a delegate whose return value is bool type and can be replaced by Func.

#Anonymous delegation

A delegate instantiated by an anonymous method is called an anonymous delegate.
Each time you instantiate a delegate, you need to define the method to be called by a delegate in advance. To simplify this process, C# 2.0 began to provide anonymous methods to instantiate delegates. In this way, when we instantiate a delegate, we can "write as you use" its instance method.
The format used is:

Delegate class name delegate instance name = delegate (args) {method body code};

In this way, the method can be written directly in the instantiation code without defining the method in another place. Of course, anonymous delegates are not suitable for the definition of delegates that require multiple methods.
Using anonymous method, the above code can be rewritten as:

MyCalculator myCal2 = delegate(int num1, int num2)
{
	//Print the name of the anonymous method
    Console.WriteLine(System.Reflection.MethodBase.GetCurrentMethod().Name);  // <Main>b__0
    return num1 + num2;
};
int num11= myCal2(1,2);//3

It should be noted that anonymous methods are not really "nameless", but the compiler automatically takes a name for us.

#Lambda expression

Although the anonymous method is very convenient to use, it's a pity that she soon became a popular Internet celebrity and didn't get coquettish for a long time. Rarely seen today, because the delegate keyword limits the extension of her use. Since C# 3.0, she has been replaced by lambda expressions, which are simpler to use. Lambda expressions are essentially improved anonymous methods.

Back to the top

1. Expression Lambda

This form can be used when the anonymous function has only one line of code. For example:

MyCalculator myCal = (num1, num2) =>  num1 + num2;
int num = myCal(1, 2);// 3

The = > symbol represents a Lambda expression, with parameters on the left and statements to be returned or executed on the right. Parameters should be placed in parentheses. If there is only one parameter, parentheses can be omitted for convenience. When there are multiple parameters or no parameters, parentheses cannot be omitted.
Compared with anonymous functions, the curly braces {} and the return keyword of the method body are omitted in the expression Lambda.

Back to the top

2. Statement Lambda

When an anonymous function has multiple lines of code, only the Lambda statement can be used.

MyCalculator myCal = (int num1, int num2)=>
{
    Console.WriteLine(System.Reflection.MethodBase.GetCurrentMethod().Name);
    return num1 + num2;
};
int num = myCal(1, 2);// 3

Lambda statements cannot omit {} and return statements.

Back to the top

3. Main uses of Lambda

In practice, Lambda expressions are mostly used in delegates, such as linq's extended query method for collection classes;
Many architectures need to call user-defined methods, which is also inseparable from delegation;
The event mechanism is based on delegation;
wait.

#Multicast delegation

When instantiating a delegate, you must register a matching function with the delegate to instantiate a delegate object. However, an instantiated delegate can register not only one function but also multiple functions. After registering multiple functions, each registered function will be executed in turn according to the registration order of the registered functions when executing the delegate.
Prototype of function registration delegate:

< delegate type > < instantiation name > + = new < delegate type > (< registration function >);

Note: delegates must be instantiated before they can use + = to register other methods. If the delegate instance registered with the function is assigned with the = sign again, it is equivalent to re instantiating the delegate, and there is no relationship between the previously registered function and the delegate instance. There are + = registered functions to delegate and - = unregistered;

< delegate type > < instantiation name > - = new < delegate type > (< registration function >);

Note: if the delegate has a return value after registering multiple functions, the return value of the last registered function will be returned when calling the delegate.

MyCalculator multiCal=new MyCalculator(Add);
multiCal += Sub;
int num1 = multiCal(1, 2); // -1
multiCal -= Sub;
int num2 = multiCal(1, 2); // 3

Keywords: C#

Added by sidwilroy on Fri, 11 Feb 2022 00:41:20 +0200