Delegation is a type. The delegation in C# is object-oriented, and it is type-safe. When a delegation instance is created, the created instance contains a call list, which can contain multiple methods. Each method is called a calling entity. The invoking entity can be either a static method or an instance method. If it is an instance method, the calling entity contains an instance that calls the instance method. The delegate does not care about the class to which the method it invokes, it only cares about whether the method being invoked is compatible with the type of delegate. Here's a code example:
1 using System; 2 namespace LycheeTest{ 3 public delegate void D(int a, int b); 4 public class Test { 5 public D myDelegate; 6 public Test() { 7 myDelegate = new D(Show1); 8 } 9 private static void Show1(int a, int b) { 10 Console.WriteLine("Method Show1 When called, the value added by the two arguments is:{0}", a + b); 11 } 12 private void Show2(int a, int b) { 13 Console.WriteLine("Method Show2 When called, the value added by the two arguments is:{0}", a + b); 14 } 15 private void Show3(int a, int b) { 16 Console.WriteLine("Method Show3 When called, the value added by the two arguments is:{0}", a + b); 17 } 18 } 19 public class Program { 20 static void Main(string[] args) { 21 Test myT = new Test(); 22 myT.myDelegate(33, 22); 23 Console.ReadKey(); 24 } 25 } 26 27 }
This code demonstrates the simplest form of delegation. Delegate types can be defined either externally or internally. This code is defined outside the class. The third line of code defines a delegate type. The key word of the delegate type is delegate, and the access modifier of the delegate type is in front of the key word. After the keyword is the return type of the delegate type, which specifies that the return type of a method compatible with the delegate type must be the same. The return type is followed by the name of the delegate type. Next comes the parameter list, which specifies that the parameter type and number of methods compatible with the delegate type must be the same. Line 5 defines a delegate type variable, which is an instance field with access rights of public. Note that the access rights of delegate type fields must be lower than those of delegate type or the same as those of delegate type. Lines 9, 12, and 15 define three methods. Line 9 is a static method. Because this code demonstrates the simplest delegation method, only static methods are used. In the construction method of line 6, the delegate type variable is instantiated. Note that adding a method to the call list of the delegate variable only needs to pass the method name to its construction method. This is the most basic way to add a call method to a delegate. Line 21 defines an instance of the Test class, and then line 22 calls the delegate member of the class. When calling a delegate member, you need to pass arguments to its list of formal parameters. This is the most basic way to use delegation. The execution results of this code are as follows:
Method Show1 is called, and the value of the two arguments added is 55:
The following is another way to use a delegate type. The example code is as follows:
1 using System; 2 namespace LycheeTest { 3 public delegate void D(int a, int b); 4 public class Test { 5 public static void Show1(int a, int b) { 6 Console.WriteLine("Method Show1 When called, the value added by the two arguments is:{0}", a + b); 7 } 8 public void Show2(int a, int b) { 9 Console.WriteLine("Method Show2 When called, the value added by the two arguments is:{0}", a + b); 10 } 11 public void Show3(int a, int b) { 12 Console.WriteLine("Method Show3 When called, the value added by the two arguments is:{0}", a + b); 13 } 14 } 15 public class Program { 16 static void Main(string[] args) { 17 Test myT = new Test(); 18 D myDelegate = new D(Test.Show1); 19 D myDelegate1 = new D(myT.Show2); 20 D myDelegate2 = new D(myT.Show3); 21 myDelegate(22, 33); 22 myDelegate1(33, 44); 23 myDelegate2(55, 66); 24 Console.ReadKey(); 25 } 26 } 27 28 }
This code cancels the delegate type field in the class and treats the delegate type as a class. In the class that contains the entry point method, first line 17 defines a variable of the Test class and instantiates it. To pass instance methods of classes to delegates, instances of classes must exist in order to reference instance methods of classes. Line 18 defines a delegate type variable and instantiates it. Note here that delegates are not a member of a class, so when passing static methods to their constructors, they need to be referenced by the class name. Line 19 also defines a delegate-type variable that needs to be referenced as an instance of a class when passing instance methods to it. Line 20 is the same as line 19. When passing a method to a delegate, you need to pass the method name instead of the method parameter list. Lines 21 to 23 are calls to delegates, passing arguments for their methods. The execution results of this code are as follows:
Method Show1 is called, and the value of the two arguments added is 55: Method Show2 is called, and the value of the two arguments added is 77: Method Show3 is called, and the value of the two arguments is 121.
Delegated access modifiers
When delegates are outside the class, the access modifiers available include public and internal. If nothing is written, the default is internal. When delegates are inside a class, access modifiers that can be used include public, protected, internal, protected
1 using System; 2 namespace LycheeTest{ 3 public class Test { 4 protected delegate void D(int a, int b); 5 private delegate void D1(int a, int b); 6 protected internal delegate void D2(int a, int b); 7 internal delegate void D3(int a, int b); 8 private D myD; 9 private D1 myD1; 10 private D2 myD2; 11 private D3 myD3; 12 public Test() { 13 myD = new D(Show1); 14 myD1 = new D1(Show1); 15 myD2 = new D2(Show1); 16 myD3 = new D3(Show1); 17 } 18 public static void Show1(int a, int b) { 19 Console.WriteLine("Method Show1 When called, the value added by the two arguments is:{0}", a + b); 20 } 21 public void Show2(int a, int b) { 22 Console.WriteLine("Method Show2 When called, the value added by the two arguments is:{0}", a + b); 23 } 24 public void Show3(int a, int b) { 25 Console.WriteLine("Method Show3 When called, the value added by the two arguments is:{0}", a + b); 26 } 27 public void Use() { 28 myD(11, 12); 29 myD1(22, 45); 30 myD2(55, 78); 31 myD3(345, 100); 32 } 33 } 34 class Test1: Test { 35 private D Test1D; 36 private D2 Test1D2; 37 private D3 Test1D3; 38 public Test1() { 39 Test1D = new D(Test.Show1); 40 Test1D2 = new D2(Test.Show1); 41 Test1D3 = new D3(Test.Show1); 42 } 43 public void Use1() { 44 Test1D(22, 45); 45 Test1D2(44, 45); 46 Test1D3(77, 78); 47 } 48 } 49 public class Program { 50 static void Main(string[] args) { 51 Test1 myT1 = new Test1(); 52 myT1.Use(); 53 myT1.Use1(); 54 Console.ReadKey(); 55 } 56 } 57 }
Line 4 of the code defines the delegate type inside the class, which is defined as a member of the class and has protected access. It can be accessed either within the class or by a derived class. The delegate type defined in line 5 of the code has private ly accessible permissions, which can only be accessed internally within this class. The delegate type of protected internal access rights defined in line 6 of the code can be accessed by this assembly and by derived classes, regardless of which assembly the derived class is located in. The delegate type defined in line 7 is internal, and it can only be accessed by this assembly. Because all these delegate types can be accessed internally within this class, lines 10 to 13 define their variables. In the instance construction method of line 12, the variables of these four delegate types are instantiated and method Show1 is added to their call list. Show1 is a static method, but you don't need to use a class name reference when passing in a delegate type constructor within a class. Line 27 defines the instance method, calls the four delegates inside the method, and passes them in arguments. Line 34 defines a class that inherits from the base class Test. Because only D, D2 and D3 delegate types in the base class can be accessed by derived classes, their variables are defined in lines 35 to 37. Note that although they are of the same type as delegate variables in the base class, they are different delegates. In the instance construction method in line 38, create instances for the three delegate types and add methods to their call lists, because the static method Show1 is also inherited by derived classes, so the method names passed in here can be referenced by class names or not. Line 43 defines an instance method that calls the three delegates internally and passes them in arguments. Line 51 defines the instance of the derived class, and then calls the instance methods Use1 and Use1. The execution results of this code are as follows:
Method Show1 is called, and the value of the two arguments added is 23: Method Show1 is called, and the value of the two arguments added is 67: Method Show1 is called, and the value of the two arguments is 133. Method Show1 is called, and the value of the two arguments added is 445. Method Show1 is called, and the value of the two arguments added is 67: Method Show1 is called, and the value of the two arguments added is 89. Method Show1 is called, and the value of the two arguments added is 155.
Because the access rights of D and D2 are defined as protected internal and protected internal. So let's verify whether they can be accessed in other assemblies. First, remove the class that contains the Main method from this code, and then change it into a class library in its project properties. Next, create a new console project and physically refer to the class library. The code for the console project is as follows:
1 using System; 2 using LycheeTest; 3 namespace LycheeTest1{ 4 class Program: Test { 5 private D pD; 6 private D2 pD2; 7 public Program() { 8 pD = new D(Show1); 9 pD2 = new D2(Show1); 10 } 11 public void Use3() { 12 pD(34, 33); 13 pD2(12, 11); 14 } 15 static void Main(string[] args) { 16 Program p = new Program(); 17 p.Use3(); 18 Console.ReadKey(); 19 } 20 } 21 }
Because the namespace of line 3 and the namespace of class libraries are two separate namespaces, their members are not in the same namespace. So when you refer to a member of another namespace in one namespace, you need to add another namespace name for reference. For the convenience of code writing, line 2 first refers to the namespace of the class library. Line 4 defines a class that inherits from the base class Test. Because it is a derived class, delegate types D and D2 can be accessed. Line 5 and line 6 define two variables, D and D2, respectively. The example construction method in line 7 instantiates these two variables and introduces them into Show1. Because the Show1 method is inherited, there is no need for class name references. Line 11 defines an instance method that calls these two delegates and passes them arguments. Line 16 defines an instance of this class and calls the instance method Use3. The execution results of this code are as follows:
Method Show1 is called, and the value of the two arguments added is 67: Method Show1 is called, and the value of the two arguments added is 23:
1 using System; 2 namespace LycheeTest { 3 public class Test { 4 protected delegate void D(int a, int b); 5 private delegate void D1(int a, int b); 6 protected internal delegate void D2(int a, int b); 7 internal delegate void D3(int a, int b); 8 private D myD; 9 private D1 myD1; 10 private D2 myD2; 11 private D3 myD3; 12 public Test() { 13 myD = new D(Show1); 14 myD1 = new D1(Show1); 15 myD2 = new D2(Show1); 16 myD3 = new D3(Show1); 17 } 18 public static void Show1(int a, int b) { 19 Console.WriteLine("Method Show1 When called, the value added by the two arguments is:{0}", a + b); 20 } 21 public void Show2(int a, int b) { 22 Console.WriteLine("Method Show2 When called, the value added by the two arguments is:{0}", a + b); 23 } 24 public void Show3(int a, int b) { 25 Console.WriteLine("Method Show3 When called, the value added by the two arguments is:{0}", a + b); 26 } 27 public void Use() { 28 myD(11, 12); 29 myD1(22, 45); 30 myD2(55, 78); 31 myD3(345, 100); 32 } 33 } 34 35 class Test1 { 36 private Test.D2 tD2; 37 private Test.D3 tD3; 38 public Test1() { 39 tD2 = new Test.D2(Test.Show1); 40 tD3 = new Test.D3(Test.Show1); 41 } 42 public void Use3() { 43 tD2(34, 33); 44 tD3(22, 21); 45 } 46 } 47 public class Program { 48 static void Main(string[] args) { 49 Test1 myT1 = new Test1(); 50 myT1.Use3(); 51 Console.ReadKey(); 52 } 53 } 54 }
In this code, the original class Test has not been modified. On line 35, a class is defined, which is independent of the Test class. Their relationship is limited to the same assembly. Lines 36 and 37 define two variables for delegate types D2 and D3. Note here that since these two classes are not inheritance relationships, references to the two delegate types in the Test class need to be made using the class name of the Test class. Line 38 is the instance construction method, in which delegates are instantiated. When you instantiate a delegate type, you still need to use the class name to reference the delegate type name, as well as the method name passed. Line 42 defines an instance method that invokes the delegate and passes arguments to it. Line 49 defines an instance of class Test 1, and then line 61 calls the instance method of the class. The execution results of this code are as follows:
Method Show1 is called, and the value of the two arguments added is 67: Method Show1 is called, and the value of the two arguments added is 43.