C# Basics - Delegate
2023-02-14 22:46:44  C#  >> Basics

Developers that are new to the .NET Core platform often struggle when deciding between a design based on delegates and a design based on events. The choice of delegates or events is often difficult, because the two language features are similar. Events are even built using the language support for delegates.

Delegate

Delegate is a type that represents references to methods with a particular parameter list and return type. When you instantiate a delegate, you can associate its instance with any method with a compatible signature and return type.

  1. When calling delegate, it will call every methods in the list.
  2. It is similar to pointer in C++ when delegate has only one method to call. But delegate is object-oriented and type-safe.
  3. Delegate can reference the methods from any class or other structures, only if they have the same return type and match the signature (including ref and out).

    In the context of method overloading, the signature of a method does not include the return value. But in the context of delegates, the signature does include the return value.

    In other words, a method must have the same return type as the delegate.

Process of a Invoking a Delegate

graph TD
    A[delegate instantiated] --> |return type and parameters| B(delegate calls the methods) 
    --> C(return to caller)

Once a delegate is instantiated, a method call made to the delegate will be passed by the delegate to that method. The parameters passed to the delegate by the caller are passed to the method, and the return value, if any, from the method is returned to the caller by the delegate.

Use Delegate

Here are some examples of using delegate.

Call a delegate

Create a WPF project (or any other project) and setup the Loaded handler function.

1
2
3
4
5
6
7
8
9
10
11
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}

private void Window_Loaded(object sender, RoutedEventArgs e)
{
}
}

Add delegate

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
delegate void MyDel(int x); // declaration of a delegate
public partial class MainWindow : Window
{
MyDel myDel; //instance of a delegate
public MainWindow()
{
InitializeComponent();
}

private void Window_Loaded(object sender, RoutedEventArgs e)
{
myDel = new MyDel(TestDelFunc);
}

private void TestDelFunc(int x)
{
MessageBox.Show("This is MyDel->TestDelFunc.");
}
}

Declaration of delegate

1
delegate void MyDel(int x);
`delegate` is the keyword.

`void` is the return type of delegate.

`MyDel` is the type of delegate.

`int x` is the paramether of delegate.

Instantiation of delegate

myDel = new MyDel(TestDelFunc);

An instantiated delegate can be invoked as instantiating a class. The difference is the parameter becomes an function, TestDelFunc.

1
2
3
4
private void TestDelFunc(int x)
{
MessageBox.Show("This is MyDel->TestDelFunc.");
}

Compared with Declaration, TestDelFunc method has the same style.

Examples of Delegate

Delegate Calls single function

Use the same example as above.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
 //keyword     delegate type 
delegate void MyDel(int x); // declaration of a delegate
public partial class MainWindow : Window
{
MyDel myDel; //instance of a delegate
public MainWindow()
{
InitializeComponent();
}

private void Window_Loaded(object sender, RoutedEventArgs e)
{
myDel = new MyDel(TestDelFunc);
}

private void TestDelFunc(int x)
{
this.tb1.Text = x.ToString();
}

private void btnDele_Click(object sender, RoutedEventArgs e)
{
myDel(123123);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="100"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button x:Name="btnDele" Content="Delegate" Grid.Row="0" Click="btnDele_Click"/>
<TextBox x:Name="tb1" Grid.Row="1" Grid.Column="0" VerticalContentAlignment="Center" HorizontalContentAlignment="Center"/>
<TextBox x:Name="tb2" Grid.Row="1" Grid.Column="1" VerticalContentAlignment="Center" HorizontalContentAlignment="Center"/>
</Grid>

With this, when click the button, myDel will call TestDelFunc with a parameter of 123123 and display in the Textbox.

Alt text

Delegate calls multiple functions

We’ve already known how to use delegate to call a single function. What if we call multiple functions. Let’s take a try.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//keyword     delegate type 
delegate void MyDel(int x); // declaration of a delegate
public partial class MainWindow : Window
{
MyDel myDel; //instance of a delegate
public MainWindow()
{
InitializeComponent();
}

private void Window_Loaded(object sender, RoutedEventArgs e)
{
//create two delegate calls for delegate instance
myDel = new MyDel(TestDelFunc);
myDel = new MyDel(TestDelFunc2);
}

private void TestDelFunc(int x)
{
this.tb1.Text = x.ToString();
}

private void TestDelFunc2(int x)
{
this.tb2.Text = "456456";
}

private void btnDele_Click(object sender, RoutedEventArgs e)
{
myDel(123123);
}
}

myDelwill call both of TestDelFunc and TestDelFunc2, and TestDelFunc2 should return 456456.

1
2
myDel = new MyDel(TestDelFunc);
myDel = new MyDel(TestDelFunc2);

Here is the result:
Alt text

As result, only 456456 is shown.

Remember that,

  1. When calling delegate, it will call every methods in the list.

TestDelFunc2 covers the result of TestDelFunc. Delegate will call method one by one in the list and display the last one.


Thus, if we want to call functions with delegate, we can use
+= to add the method in the list.
-= to remove the method from the list.

Add a method

+= to add the method in the list.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//keyword     delegate type 
delegate void MyDel(int x); // declaration of a delegate
public partial class MainWindow : Window
{
MyDel myDel; //instance of a delegate
public MainWindow()
{
InitializeComponent();
}

private void Window_Loaded(object sender, RoutedEventArgs e)
{
// Use += to add TestDelFunc2 into the method list.
myDel = new MyDel(TestDelFunc);
myDel += TestDelFunc2;
}

private void TestDelFunc(int x)
{
this.tb1.Text = x.ToString();
}

private void TestDelFunc2(int x)
{
this.tb2.Text = "456456";
}

private void btnDele_Click(object sender, RoutedEventArgs e)
{
myDel(123123);
}
}

Alt text

Remove a method

-= to remove the method from the list.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
//keyword     delegate type 
delegate void MyDel(int x); // declaration of a delegate
public partial class MainWindow : Window
{
MyDel myDel; //instance of a delegate
public MainWindow()
{
InitializeComponent();
}

private void Window_Loaded(object sender, RoutedEventArgs e)
{
myDel = new MyDel(TestDelFunc);
myDel += TestDelFunc2;
}

private void TestDelFunc(int x)
{
this.tb1.Text = x.ToString();
}

private void TestDelFunc2(int x)
{
this.tb2.Text = 456456.ToString();
}

private void btnDele_Click(object sender, RoutedEventArgs e)
{
myDel(123123);
}
// click to remove method from list
private void btnRemvDele_Click(object sender, RoutedEventArgs e)
{
myDel -= TestDelFunc2;
}
}

Alt text


Good Day
😎