委托和事件是.Net 框架的重要组成部分,在GUI程序开发中,大量使用了事件处理,但是亲们,对于委托,我们是否还记得曾经在书上看到的详细内容。委托的使用注意事项是什么?我们会使用委托和事件,但是我们不了解事件背后的原理,亲们,我们忘记委托了吗?反正我是忘记了。
委托是方法调用的指针,也称为函数指针,是一种特殊的对象类型,包含的是方法的地址。注意是地址,在.Net 中,委托不同于c++中的函数指针,在C#中 委托是类型安全的操作,这意味着什么呢?这意味着我们定义的方法签名必须和委托的定义类型以及返回值一致,否则会出现编译错误的提示信息。
如何定义一个委托:
delegate int AddDelegate(int x,int y); 委托的定义和类的定义一致,在任何可以定义类的地方都可以定义委托,另外,委托 的实现是继承自MulticastDelegate 类的,也就是说定义一个委托,基本上等价于定义一个新类。委托类似于方法的定义,但是没有方法体,只有方法签名,另外在方法签名的前面添加了delegate关键字。
定义一个简单的委托实例:
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 Test t = new Test(); 6 t.Execute(3, 5); 7 Console.Read(); 8 } 9 } 10 11 public class Test 12 { 13 /// <summary> 14 /// 在类的内部定义了一个委托 15 /// </summary> 16 /// <param name="x"></param> 17 /// <param name="y"></param> 18 /// <returns></returns> 19 delegate int AddDelegate(int x, int y); 20 /// <summary> 21 /// 实例方法Add1 和委托具有相同的方法签名 22 /// </summary> 23 /// <param name="x"></param> 24 /// <param name="y"></param> 25 /// <returns></returns> 26 public int Add1(int x, int y) 27 { 28 Console.WriteLine("Add1计算得到的值为" + (x + y)); 29 return x + y; 30 } 31 /// <summary> 32 /// 静态方法Add2 和委托具有相同的方法签名 33 /// </summary> 34 /// <param name="x"></param> 35 /// <param name="y"></param> 36 /// <returns></returns> 37 public static int Add2(int x, int y) 38 { 39 Console.WriteLine("Add2计算得到的值为" + (x + y * 2)); 40 return x + y * 2; 41 } 42 43 public void Execute(int x, int y) 44 { 45 AddDelegate addDelegate = new AddDelegate(Add1); 46 addDelegate += Add2; //通过+= 多播委托 ,表示一个委托实例具有多个方法,多播委托不能保证方法的执行顺序。 47 addDelegate(x, y); 48 } 49 /// <summary> 50 /// 多播委托在其中一个调用方法出现异常时会停止迭代,我们优先采用下面方法,即逐个的调用 方法列表执行,可以避免由于某个调用方法出现异常,导致其他调用方法无法执行的问题 51 /// </summary> 52 /// <param name="x"></param> 53 /// <param name="y"></param> 54 public void Execute3(int x, int y) 55 { 56 AddDelegate addDelegate = new AddDelegate(Add1); 57 addDelegate += Add2; //通过+= 多播委托 ,表示一个委托实例具有多个方法,多播委托不能保证方法的执行顺序。 58 if (addDelegate != null) 59 { 60 Delegate[] addArr = addDelegate.GetInvocationList();//获取多播委托的调用列表 61 if (addArr != null) 62 { 63 foreach (var item in addArr) 64 { 65 ((AddDelegate)item)(x, y); 66 } 67 } 68 } 69 } 70 public void Execute1(int x, int y) 71 { 72 AddDelegate addDelegate = Add1;//直接为委托实例赋值方法签名 称为委托推断 .Net 编译器会识别委托签名 73 addDelegate += Add2; 74 addDelegate(x, y); 75 } 76 }
实例总结:
- addDelegate =Add1 这种为委托实例赋值的方式称为 委托推断,是由.Net 编译器自动推断方法签名是否符合委托。
- 委托可以通过+= 来实现一个委托实例具有多个调用方法执行,请注意,多播委托的执行不能保证顺序,即不能保证方法注册的次序和调用次序一致。2.多播委托在执行过程中,如果某个调用方法出现异常,则整个多播委托的调用方法列表都会停止执行,我们可以采用Execute3 展示的那样,逐个手动执行调用方法列表,避免由于某个调用方法出现异常,导致其他调用方法不会执行的问题。
- 委托实例的方法即可以是实例方法,又可以是静态方法,只要保证方法签名一致就可以正常的调用。
- 委托可以作为参数传递,下面我们会展示一个通过将委托作为实例,来实现排序的功能。
- 委托可以具有访问修饰符public、protected以及private等。
- 可以通过委托的简便方式,匿名方法来直接定义一个方法到委托实例,.Net 编译器会帮助我们生成一个随机的方法名称,只是我们不需要。
- lambda 也可以应用到委托中,通过lambda表达式生成的是匿名方法,或者说是匿名委托。
我们来定义一个将委托作为参数传递,实现排序的功能。目前我们的需求是创建一个通用类库,其中有一个可以将数组中的内容按升序排列,但是数组的数据类型我们可以自定义,也就是说,我们不能指定数组的数据类型为某一种具体类型,可以为int、double ,也可以为类。
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 //Test t = new Test(); 6 //t.Execute(3, 5); 7 List<string> listInt = new List<string>(); 8 9 List<Employee> listEmployee = new List<Employee>(); 10 for (int i = 10; i >= 0; i--) 11 { 12 listInt.Add(i.ToString()); 13 Employee employee = new Employee() 14 { 15 Name = i.ToString(), 16 Salary = i 17 }; 18 listEmployee.Add(employee); 19 } 20 Test1 test1 = new Test1(); 21 Employee[] arrEmployee = listEmployee.ToArray(); 22 test1.Sort(arrEmployee, test1.CompareEmployee); 23 24 foreach (var item in arrEmployee) 25 { 26 Console.WriteLine(item.Salary); 27 } 28 29 string[]arrInt=listInt.ToArray(); 30 test1.Sort(arrInt, test1.CompareInt); 31 foreach (var item in arrInt) 32 { 33 Console.WriteLine(item); 34 } 35 36 37 38 Console.Read(); 39 } 40 } 41 public class Test1 42 { 43 44 /// <summary> 45 /// 委托 比较两个值的大小 x>y 返回true 46 /// </summary> 47 /// <param name="x"></param> 48 /// <param name="y"></param> 49 /// <returns></returns> 50 public delegate bool Compare(object x, object y); 51 52 public void Sort(object[] arrObj, Compare compare) 53 { 54 for (int i = 0; i < arrObj.Length; i++) 55 { 56 for (int j = i+1; j < arrObj.Length; j++) 57 { 58 if (compare(arrObj[i], arrObj[j])) 59 { 60 object temp = arrObj[i]; 61 arrObj[i] = arrObj[j]; 62 arrObj[j] = temp; 63 } 64 } 65 } 66 } 67 68 69 public bool CompareEmployee(object em1, object em2) 70 { 71 if (em1 != null && em2 != null) 72 { 73 return ((Employee)em1).Salary > ((Employee)em2).Salary; 74 } 75 return false; 76 } 77 78 public bool CompareInt(object x, object y) 79 { 80 return Convert.ToInt32(x) > Convert.ToInt32(y); 81 } 82 83 } 84 /// <summary> 85 /// 员工类 86 /// </summary> 87 public class Employee 88 { 89 /// <summary> 90 /// 员工姓名 91 /// </summary> 92 public string Name { get; set; } 93 /// <summary> 94 /// 员工工资 95 /// </summary> 96 public int Salary { get; set; } 97 }
总结:
- 将委托作为参数进行传递,实现了泛型委托可以实现的功能。
- 可以在子类实现具体的功能,然后传递到对应的方法执行,可以有效的实现可扩展性,同时避免对其他功能的干扰。
委托作为事件的基础,本身 只具有简单的一些概念,但是我觉得要真正的在实际项目中可以灵活的使用,还是需要深入思考,做个好梦
我又回来了,回到了技术最前线,