• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

C#:委托

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递,这种将方法动态地赋给参数的做法,可以避免在程序中大量使用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 method
13             GetAString firstStringMethod = new GetAString(balance.ToString);
14             Console.WriteLine("String is {0}", firstStringMethod());
15 
16             // firstStringMethod references a static method
17             firstStringMethod = new GetAString(Currency.GetCurrencyUnit);
18             Console.WriteLine("String is {0}", firstStringMethod());
19 
20             Console.ReadLine();
21         }
22     }
23 
24     struct Currency
25     {
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             checked
47             {
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 MathsOperation
27     {
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]是一个委托,其语法是:

  • operations[i]表示“这个委托”。换言之,就是委托代表的方法
  • operations[i](2.0)表示“调用这个方法,参数放在括号中”。

示例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 BubbleSorter
21     {
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 Employee
40     {
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 MathOperations
25     {
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             try
13             {
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 }

输出:
委托只调用了第一个方法,第一个方法抛出了异常,所以委托的迭代会停止,不再调用Two()方法。

为了避免这个问题,应手动迭代方法列表。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                 try
17                 {
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时,不是传送已知的方法名,而是使用一个简单的代码块。
在使用匿名方法是,必须遵循两个规则:在匿名方法中不能使用跳转语句调到该匿名方法的外部;反之亦然:匿名方法外部的跳转语句不能调到该匿名方法的内部。

  • 在匿名方法内部不能访问不完全的代码
  • 不能访问在匿名方法外部使用的ref和out参数,但可以使用在匿名方法外部定义的其他变量
  • 如果需要用匿名方法多次编写同一个功能,就不要使用匿名方法,而编写一个指定的方法比较好,因为该方法只能编写一次,以后可通过名称引用它

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         }

                      

鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
C#反射入门知识发布时间:2022-07-13
下一篇:
C#中TreeView的CheckBox的两种级联选择发布时间:2022-07-13
热门推荐
热门话题
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap