委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递,这种将方法动态地赋给参数的做法,可以避免在程序中大量使用If-Else(Switch)语句,同时使得程序具有更好的扩展性。
与使用一个类相似,在使用委托时,也需要经过两个步骤:
delegate void IntMethodInoker(int x);
1 using System; 2 3 namespace ConsoleApplication25 4 { 5 class Program 6 { 7 private delegate string GetAString(); 8 static void Main(string[] args) 9 {10 int x = 40;11 GetAString firstStringMethod = new GetAString(x.ToString);12 Console.WriteLine("String is {0}",firstStringMethod());13 Console.ReadLine();14 }15 }16 17 18 }
提示:给定委托的实例可以表示任何类型的任何对象上的实例方法或静态方法--只要方法的签名匹配于委托的签名即可。
下面的示例,让firstStringMethod委托在另一个对象上调用其他两个方法,其中一个是实例方法,另一个是静态方法。
1 using System; 2 3 namespace ConsoleApplication26 4 { 5 class Program 6 { 7 private delegate string GetAString(); 8 static void Main(string[] args) 9 {10 Currency balance = new Currency(34, 50);11 12 // firstStringMethod references an instance method13 GetAString firstStringMethod = new GetAString(balance.ToString);14 Console.WriteLine("String is {0}", firstStringMethod());15 16 // firstStringMethod references a static method17 firstStringMethod = new GetAString(Currency.GetCurrencyUnit);18 Console.WriteLine("String is {0}", firstStringMethod());19 20 Console.ReadLine();21 }22 }23 24 struct Currency25 {26 public uint Dollars;27 public ushort Cents;28 public Currency(uint dollars, ushort cents)29 {30 this.Dollars = dollars;31 this.Cents = cents;32 }33 34 public override string ToString()35 {36 return string.Format("${0}.{1,-2:00}", Dollars, Cents);37 }38 39 public static string GetCurrencyUnit()40 {41 return "Dollar";42 }43 44 public static explicit operator Currency(float value)45 {46 checked47 {48 uint dollars = (uint)value;49 ushort cents = Convert.ToUInt16((value - dollars) * 100);50 return new Currency(dollars, cents);51 }52 }53 54 public static implicit operator float(Currency value)55 {56 return value.Dollars + (value.Cents / 100.0f);57 }58 59 public static implicit operator Currency(uint value)60 {61 return new Currency(value, 0);62 }63 64 public static implicit operator uint(Currency value)65 {66 return value.Dollars;67 }68 }69 }
示例2:
1 using System; 2 3 namespace ConsoleApplication28 4 { 5 class Program 6 { 7 delegate double DoubleOp(double x); 8 static void Main(string[] args) 9 {10 DoubleOp[] operations = { MathsOperation.MultiplyByTwo, MathsOperation.Square };11 for (int i = 0; i < operations.Length; i++)12 {13 Console.WriteLine("Using operations[{0}]:", i);14 ProcessAndDisplayNumber(operations[i], 3.0);15 }16 Console.ReadLine();17 }18 19 static void ProcessAndDisplayNumber(DoubleOp action, double value)20 {21 double result = action(value);22 Console.WriteLine("Value is {0}, result of the operation is {1}", value, result);23 }24 }25 26 class MathsOperation27 {28 public static double MultiplyByTwo(double value)29 {30 return value * 2;31 }32 33 public static double Square(double value)34 {35 return value * value;36 }37 }38 }
示例2的关键一行是把委托传递给ProcessAndDisplayNumber()方法
ProcessAndDisplayNumber(operations[i], 3.0);
其中传递了委托名,但不带任何参数,假定operations[i]是一个委托,其语法是:
示例3:
1 using System; 2 3 namespace ConsoleApplication29 4 { 5 delegate bool Comparison(object x, object y); 6 class Program 7 { 8 static void Main(string[] args) 9 {10 Employee[] employees = { new Employee("a", 20000), new Employee("b", 10000), new Employee("c", 15000) };11 BubbleSorter.Sort(employees, new Comparison(Employee.CompareSalary));12 foreach (var employee in employees)13 {14 Console.WriteLine(employee);15 }16 Console.ReadLine();17 }18 }19 20 class BubbleSorter21 {22 static public void Sort(object[] sortArray, Comparison comparison)23 {24 for (int i = 0; i < sortArray.Length; i++)25 {26 for (int j = i; j < sortArray.Length; j++)27 {28 if (comparison(sortArray[j], sortArray[i]))29 { 30 object temp=sortArray[i];31 sortArray[i]=sortArray[j];32 sortArray[j]=temp;33 }34 }35 }36 }37 }38 39 class Employee40 {41 private string name;42 private decimal salary;43 public Employee(string name, decimal salary)44 {45 this.name = name;46 this.salary = salary;47 }48 49 public override string ToString()50 {51 return string.Format("{0},{1:C}", name, salary);52 }53 54 public static bool CompareSalary(object x, object y)55 {56 Employee e1 = (Employee)x;57 Employee e2 = (Employee)y;58 return (e1.salary < e2.salary);59 }60 }61 }
输出:
多播委托:当一个委托只包含一个方法调用,调用委托的次数与调用方法的次数相同,如果调用多个方法,就需要多次显示调用这个委托。委托也可以包含多个方法,这种委托成为多播委托。
如果调用多播委托,就可以按顺序连续调用多个方法。为此,委托的签名就必须返回void,否则就只能得到委托调用的最后一个方法的结果。
1 using System; 2 3 namespace ConsoleApplication30 4 { 5 delegate void DoubleOp(double value); 6 class Program 7 { 8 static void Main(string[] args) 9 {10 DoubleOp operations = new DoubleOp(MathOperations.MultiplyByTwo);11 operations += MathOperations.Square;12 ProcessAndDisplayNumber(operations, 3.0);13 Console.ReadLine();14 }15 16 static void ProcessAndDisplayNumber(DoubleOp action, double valueToProcess)17 {18 Console.WriteLine();19 Console.WriteLine("ProcessAndDisplayNumber called with value = {0}", valueToProcess);20 action(valueToProcess);21 }22 }23 24 class MathOperations25 {26 public static void MultiplyByTwo(double value)27 {28 double result = value * 2;29 Console.WriteLine("Multiplying by 2: {0} gives {1}", value, result);30 }31 32 public static void Square(double value)33 {34 double result = value * value;35 Console.WriteLine("Squaring: {0} gives {1}", value, result);36 }37 }38 }
注意:如果使用多播委托,就应注意对同一个委托调用方法链的顺序并未正式定义,一次应避免编写依赖于以特定顺序调用方法的代码。
通过一个委托调用多个方法还有一个问题,多播委托包含一个诸葛调用的委托集合,如果通过委托调用的一个方法抛出了异常,整个迭代就会停止。例如:
1 using System; 2 3 namespace ConsoleApplication31 4 { 5 public delegate void DemoDelegate(); 6 class Program 7 { 8 static void Main(string[] args) 9 {10 DemoDelegate dl = new DemoDelegate(One);11 dl += Two;12 try13 {14 dl();15 }16 catch(Exception)17 {18 Console.WriteLine("Exception caught");19 }20 Console.ReadLine();21 }22 23 static void One()24 {25 Console.WriteLine("One");26 throw new Exception("Error in one");27 }28 29 static void Two()30 {31 Console.WriteLine("Two");32 }33 }34 }
输出:
为了避免这个问题,应手动迭代方法列表。Delegate类定义了方法GetInvocationList(),它返回一个Delegate对象数组。
1 using System; 2 3 namespace ConsoleApplication31 4 { 5 public delegate void DemoDelegate(); 6 class Program 7 { 8 static void Main(string[] args) 9 {10 DemoDelegate dl = new DemoDelegate(One);11 dl += new DemoDelegate(Two);12 Delegate[] delegates = dl.GetInvocationList();13 for (int i = 0; i < delegates.Length; i++)14 {15 DemoDelegate d = (DemoDelegate)delegates[i];16 try17 {18 d();19 }20 catch (Exception)21 {22 Console.WriteLine("Exception caught");23 }24 }25 Console.ReadLine();26 }27 28 static void One()29 {30 Console.WriteLine("One");31 throw new Exception("Error in one");32 }33 34 static void Two()35 {36 Console.WriteLine("Two");37 }38 }39 }
输出:
匿名方法:到目前为止,要想使委托工作,方法必须已经存在。但是使用委托还有另外一种方式:即通过匿名方法。匿名方法是用做委托参数的一个代码块。
用匿名方法定义委托的语法和前面的定义并没有区别,但是实例化委托时就有区别了。
1 using System; 2 3 namespace ConsoleApplication32 4 { 5 class Program 6 { 7 delegate string DelegateTest(string val); 8 static void Main(string[] args) 9 {10 string mid = ", middle part,";11 DelegateTest anonDel = delegate(string param)12 {13 param += mid;14 param += " and this was added to the string.";15 return param;16 };17 18 Console.WriteLine(anonDel("Start of string"));19 Console.ReadLine();20 }21 }22 }
在Main方法中,定义anonDel时,不是传送已知的方法名,而是使用一个简单的代码块。
在使用匿名方法是,必须遵循两个规则:在匿名方法中不能使用跳转语句调到该匿名方法的外部;反之亦然:匿名方法外部的跳转语句不能调到该匿名方法的内部。
l表达式 : 为匿名方法提供了的一个新语法。
1 using System; 2 3 namespace ConsoleApplication33 4 { 5 class Program 6 { 7 delegate string DelegateTest(string val); 8 static void Main(string[] args) 9 {10 string mid = ", middle part,";11 DelegateTest anonDel = param =>12 {13 param += mid;14 param += " and this was added to the string.";15 return param;16 };17 Console.WriteLine(anonDel("Start of the string"));18 Console.ReadLine();19 }20 }21 }
运算符=>的左边列出了匿名方法需要的参数。有几种编写方式:
1. 在括号中定义类型和变量名:
(string param) =>
2. 省去变量类型:
(param) =>
3. 如果只有一个参数,就可以删除括号:
DelegateTest anonDel = param =>
表达式的右边列出了实现代码。如果实现代码只有一行,也可以删除花括号和return语句。
利用l表达式 来写前面的多播委托示例,优点是它删除了类:
1 using System; 2 3 namespace ConsoleApplication34 4 { 5 class Program 6 { 7 delegate double DoubleOp(double value); 8 static void Main(string[] args) 9 {10 DoubleOp multByTwo = val => val * 2;11 DoubleOp square = val => val * val;12 13 DoubleOp[] operations = { multByTwo, square };14 15 for (int i = 0; i < operations.Length; i++)16 {17 Console.WriteLine("Using operations[{0}]", i);18 ProcessAndDiaplayNumber(operations[i], 3.0);19 }20 21 Console.ReadLine();22 }23 24 static void ProcessAndDiaplayNumber(DoubleOp action, double value)25 {26 double result= action(value);27 Console.WriteLine("The result of the value: {0} is {1}", value, result);28 }29 }30 }
返回类型协变:方法的返回类型可以派生于委托定义的类型。在下面的示例中,委托MyDelegate1定义为返回DelegateReturn类型。赋予委托实例d1的方法Method1返回DelegateReturn2类型,DelegateReturn2派生于DelegateReturn,因此满足了委托的需求,这成为返回类型协变。
1 namespace ConsoleApplication35 2 { 3 public class DelegateReturn 4 { 5 } 6 7 public class DelegateReturn2 : DelegateReturn 8 { 9 }10 11 public delegate DelegateReturn MyDelegate1();12 13 class Program14 {15 static void Main()16 {17 MyDelegate1 d1 = Method1;18 d1();19 }20 21 static DelegateReturn2 Method1()22 {23 DelegateReturn2 d2 = new DelegateReturn2();24 return d2;25 }26 }27 }
参数类型协变:表示委托定义的参数可能不同于委托调用的方法。这里是返回类型不同,因为方法使用的参数类型可能派生自委托定义的类型。
1 namespace ConsoleApplication36 2 { 3 public class DelegateParam 4 { } 5 6 public class DelegateParam2 : DelegateParam 7 { } 8 9 public delegate void MyDelegate2(DelegateParam2 p);10 class Program11 {12 static void Main(string[] args)13 {14 MyDelegate2 d2 = Method2;15 DelegateParam2 p = new DelegateParam2();16 d2(p);17 }18 19 static void Method2(DelegateParam p)20 { }21 }22 }
联系客服