1,委托的概念
当需要把方法传递给其他方法时,需要调用委托。委托只是一种特殊类型的对象,其特殊之处在于,我们之前定义的所有对象都包含数据,而委托包含的只是一个或多个方法的地址。
委托的类型安全性非常高。在定义委托时,必须给出它所表示的方法的签名和返回类型等全部细节。
2,使用委托的场景
给一个方法传递的参数也是方法,而且编译时不知道第二个方法是什么,这个信息只能在运行时得到,所以把第二个方法作为参数传递给第一个方法。
3,简单的例子
public static string StrAddTwo(string x) { return x + 2; } public static string StrAddThree(string x) { return x + 3; } public delegate string StrAdd(string x); static void Main(string[] args) { StrAdd strAdd = StrAddTwo; Console.WriteLine(strAdd("Test")); strAdd = StrAddThree; Console.WriteLine(strAdd("Test")); Console.ReadKey(); }
4,更贴切点的例子
然而,委托并不是为了这些简单的操作而设计的,所以,必须用一个足够复杂的例子才能体现委托的必要。
首先,看一个给int数组进行冒泡排序的方法,代码如下:
static void Main(string[] args) { int[] arr = new[] {5, 2, 4, 9, 15}; Sort(arr); Console.ReadKey(); } static void Sort(int[] sortArray) { bool swapped = true; do { swapped = false; for (int i = 0; i < sortArray.Length-1; i++) { if (sortArray[i]>sortArray[i+1]) { int temp = sortArray[i]; sortArray[i] = sortArray[i + 1]; sortArray[i + 1] = temp; swapped = true; } } } while (swapped); //查看排序结果 foreach (var item in sortArray) { Console.WriteLine(item); } }
这个方法只适合于int类型,但我们希望Sort()方法能给任何对象排序,那么上面代码中的if (sortArray[i]>sortArray[i+1])就有问题了,因为它需要比较数组中的两个对象,看看哪一个更大。可以对int进行这样的比较,对如何对没有实现“<”运算符的新类进行比较?答案是能识别该类的客户端代码必须在委托中传递一个封装的方法,这个方法可以进行比较。
public class BuddleSorter { public static void Sort<T>(IList<T> sortArray, Func<T, T, bool> comparison) { bool swapped; do { swapped = false; for (int i = 0; i < sortArray.Count-1; i++) { if (comparison(sortArray[i + 1], sortArray[i])) { T temp = sortArray[i]; sortArray[i] = sortArray[i + 1]; sortArray[i + 1] = temp; swapped = true; } } } while (swapped); } }
再定义一个类,并实现它的comparison方法:
class Employee { public Employee(string name, decimal salary) { Name = name; Salary = salary; } public string Name { get; set; } public decimal Salary { get; set; } public override string ToString() { return string.Format($"{Name},{Salary:C}"); } public static bool CompareSalary(Employee e1, Employee e2) { return e1.Salary < e2.Salary; } }
测试一下:
static void Main(string[] args) { Employee[] employees = { new Employee("Bugs Bunny",20000), new Employee("Elmer Fudd",10000), new Employee("Daffy",25000), new Employee("Wile",100000.38m), new Employee("Forhorn",23000), new Employee("Road Runner",50000) }; BuddleSorter.Sort(employees, Employee.CompareSalary); foreach (var employee in employees) { Console.WriteLine(employee); } Console.ReadKey(); }
输出结果:
Elmer Fudd,¥10,000.00
Bugs Bunny,¥20,000.00
Forhorn,¥23,000.00
Daffy,¥25,000.00
Road Runner,¥50,000.00
Wile,¥100,000.38
5,多播委托
多播委托可以包含多个方法,这样的委托称为多播委托。如果调用多播委托,就可以按照顺序调用多个方法。示例代码:
Action<double> operations = MathOperations.MultiplyByTwo; operations += MathOperations.Square; ProcessAndDispayNumber(operations, 2.0); ProcessAndDispayNumber(operations,7.94); ProcessAndDispayNumber(operations,1.414); Console.ReadLine();
多播委托的缺点:如果其中一个方法,如果其中一个方法抛出异常,委托迭代将会终止,不会执行后面的方法。这时需要调用Delegate类定义的GetInvocationList()方法,它返回一个Delegate对象数组,现在可以使用这个委托调用与委托直接相关的方法,捕获异常,并继续下一次迭代。
6,匿名方法
匿名方法就是用作委托的参数的一段代码。如下所示:
string mid = ",middle part,"; Func<string, string> anonDel = delegate(string param) { param += mid; param += " and this was added to the string."; return param; }; Console.WriteLine(anonDel("Start of string"));
7,lambda表达式以及闭包
double d = 4; Func<double, double> oneParams = (x) => x * d; Console.WriteLine(oneParams(3)); //输出12
8,使用foreach语句的闭包
var values = new List<int>() {1,2,3}; var funcs = new List<Func<int>>(); foreach (var value in values) { funcs.Add(()=>value); } foreach (var f in funcs) { Console.WriteLine(f()); }
输出:
1
2
3
这和c#4.0是不一样的,需要注意
9,事件
事件基于委托,为委托提供了一种发布/订阅机制。
1)事件发布程序
using System; namespace Demo.Delegates { public class CarInfoEventArgs : EventArgs { public CarInfoEventArgs(string car) { this.Car = car; } public string Car { get; set; } } public class CarDealer { public event EventHandler<CarInfoEventArgs> NewCarInfo; public void NewCar(string car) { Console.WriteLine("CarDealer,new car {0}",car); RaiseNewCarInfo(car); } protected virtual void RaiseNewCarInfo(string car) { EventHandler<CarInfoEventArgs> newCarInfo = NewCarInfo; if (newCarInfo != null) { newCarInfo(this,new CarInfoEventArgs(car)); } } } }
2)事件侦听器
using System; namespace Demo.Delegates { public class Consumer { private readonly string name; public Consumer(string name) { this.name = name; } public void NewCarIsHere(object sender, CarInfoEventArgs e) { Console.WriteLine($"{name}: car {e.Car} is new"); } } }
3)测试一下:
static void Main(string[] args) { var dealer = new CarDealer(); var michael = new Consumer("Michael"); dealer.NewCarInfo += michael.NewCarIsHere; dealer.NewCar("Ferrari"); var sebastian = new Consumer("Sebastian"); dealer.NewCarInfo += sebastian.NewCarIsHere; dealer.NewCar("Mercedes"); dealer.NewCarInfo -= michael.NewCarIsHere; dealer.NewCar("Red Bull Racing"); Console.ReadKey(); }
输出结果:
CarDealer,new car Ferrari Michael: car Ferrari is new CarDealer,new car Mercedes Michael: car Mercedes is new Sebastian: car Mercedes is new CarDealer,new car Red Bull Racing Sebastian: car Red Bull Racing is new
10,弱事件
using System.Windows; namespace Demo.Delegates { public class WeakCarInfoEventManager:WeakEventManager { public static void AddListener(object source, IWeakEventListener listener) { CurrentManager.ProtectedAddListener(source, listener); } public static void RemoveListener(object source, IWeakEventListener listener) { CurrentManager.ProtectedRemoveListener(source,listener); } public static WeakCarInfoEventManager CurrentManager { get { var manager = GetCurrentManager(typeof(WeakCarInfoEventManager)) as WeakCarInfoEventManager; if (manager == null) { manager = new WeakCarInfoEventManager(); SetCurrentManager(typeof(WeakCarInfoEventManager),manager); } return manager; } } void CarDealer_NewCarInfo(object sender, CarInfoEventArgs e) { DeliverEvent(sender,e); } protected override void StartListening(object source) { (source as CarDealer).NewCarInfo += CarDealer_NewCarInfo; } protected override void StopListening(object source) { (source as CarDealer).NewCarInfo -= CarDealer_NewCarInfo; } } }
修改事件监听器:
using System; using System.Windows; namespace Demo.Delegates { public class Consumer:IWeakEventListener { private readonly string name; public Consumer(string name) { this.name = name; } public void NewCarIsHere(object sender, CarInfoEventArgs e) { Console.WriteLine($"{name}: car {e.Car} is new"); } public bool ReceiveWeakEvent(Type managerType, object sender, EventArgs e) { NewCarIsHere(sender,e as CarInfoEventArgs); return true; } } }
测试一下:
static void Main(string[] args) { var dealer = new CarDealer(); var michael = new Consumer("Michael"); WeakCarInfoEventManager.AddListener(dealer,michael); dealer.NewCar("Ferrari"); var sebastian = new Consumer("Sebastian"); WeakCarInfoEventManager.AddListener(dealer, sebastian); dealer.NewCar("Mercedes"); WeakCarInfoEventManager.RemoveListener(dealer,michael); dealer.NewCar("Red Bull Racing"); Console.ReadKey(); }
11,泛型弱事件管理器
使用这个类时,不再需要为每个事件实现一个自定义的弱事件管理器,也不需要让事件的消费者实现接口IWeakEventsListener。
static void Main(string[] args) { var dealer = new CarDealer(); var michael = new Consumer("Michael"); WeakEventManager<CarDealer,CarInfoEventArgs>.AddHandler(dealer, "NewCarInfo",michael.NewCarIsHere); dealer.NewCar("Ferrari"); var sebastian = new Consumer("Sebastian"); WeakEventManager<CarDealer, CarInfoEventArgs>.AddHandler(dealer, "NewCarInfo", sebastian.NewCarIsHere); dealer.NewCar("Mercedes"); WeakEventManager<CarDealer, CarInfoEventArgs>.RemoveHandler(dealer, "NewCarInfo", sebastian.NewCarIsHere); dealer.NewCar("Red Bull Racing"); Console.ReadKey(); }