using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Delegate1 { // 自定义委托 public delegate double Calc(double x, double y); class Program { static void Main(string[] args) { Calculator cal = new Calculator(); // 使用C#中2中最常用的定义的委托类型 Action actShowMsg = new Action(cal.ShowMessage); // Action委托用于封装不带参数不带返回值的方法(实例/静态) // 使用委托间接调用Calculator的ShowMessage方法 actShowMsg.Invoke(); // 简便的调用写法,类似函数指针 actShowMsg(); Func<int, int, int> funcAdd = new Func<int, int, int>(cal.Add); // Func是一个泛型委托,可以用于封装具有参数和返回值的方法 Console.WriteLine("88 + 99 = {0}", funcAdd(88, 99)); Console.WriteLine("88 + 99 = {0}", funcAdd.Invoke(88, 99)); // 使用自定义委托 Calc dcal = new Calc(cal.Div); Console.WriteLine("150 / 99 = {0}", dcal.Invoke(150, 99)); ProductFactory prodfac = new ProductFactory(); WrapFactory wrapfac = new WrapFactory(); Func<Product> getProdFunc1 = new Func<Product>(prodfac.MakePizza); Func<Product> getProdFunc2 = new Func<Product>(prodfac.MakeToyCar); Logger logger = new Logger(); Action<Product> actLog = new Action<Product>(logger.Log); Box box1 = wrapfac.WrapProduct(getProdFunc1, actLog); Box box2 = wrapfac.WrapProduct(getProdFunc2, actLog); Console.WriteLine("Product1 name:{0}", box1.Prod.Name); Console.WriteLine("Product2 name:{0}", box2.Prod.Name); } } #if false 1、什么是C#中的委托 委托是C/C++中函数指针的升级版 2、一切皆地址 程序 = 数据 + 算法, 变量(即用于存储程序中的数据)本质:是以变量名所对应的内存地址为起点的一段内存,在这段内存中所存储的是变量的数据,这段内存的大小则由变量的数据类型决定。(变量名只是一段内存的标识) 函数(即程序中的算法)本质: 是以函数名名所对应的内存地址为起点的一段内存,在这段内存中所存储的是一组机器语言指令。(函数名也只是一段存储指令的内存的标识) 3、直接调用和间接调用的本质 直接调用:直接通过函数名来调用函数,CPU通过函数名直接找到函数所在地址执行里面的机器语言指令,执行完成后返回到调用者。 间接调用:通过函数指针来调用函数,CPU通过读取函数指针变量中存储的函数起始地址,然后执行地址对应内存里面的机器语言指令,执行完成后返回到调用者。相比于直接调用只是多了一个从函数指针读取函数地址的过程。 4、自定义委托 委托也是一种类类型 委托的声明和一般类的不一样,主要是为了更好的可读性和保留C/C++的传统,类似C++函数指针的声明形式,使用delegate关键字 注意委托声明的位置,它本身是一种类型,一般声明在名称空间下与其他的类型平级,如果声明在类中则成为嵌套类型。 委托与封装的方法必须"类型兼容"即返回值、参数列表的类型需要一致 5、委托的一般使用 实例:把方法当作参数传给另一个方法,分为模板方法、回调方法2种情形 模板方法:其他逻辑已经确定,其中有一个步骤依赖委托的结果,一般出现在代码的中间部分 回调方法:一般位于代码的最后部分,根据一定条件决定委托是否被触发调用 6、注意 》委托是一种方法级别的紧耦合 》使用不当会使可读性下降、debug难度增加 》如果把委托回调、异步调用和多线程纠缠在一起,会让代码变得难以阅读和维护 》委托使用不当有可能造成内存泄漏和程序性能下降(因为如果委托了一个实例方法,只要委托引用了这个方法,则这个方法对应的实例就不能释放) #endif class Calculator { public void ShowMessage() { Console.WriteLine("This is a simple calculator!"); } public int Add(int lhs, int rhs) { return lhs + rhs; } public double Div(double lhs, double rhs) { return lhs / rhs; } } class Logger { public void Log(Product prod) { Console.WriteLine("Product '{0}' created at {1}. Price is {2}", prod.Name, DateTime.UtcNow, prod.Price); } } class Product { public string Name { get; set; } public double Price { get; set; } } class Box { public Product Prod { get; set; } } class WrapFactory { public Box WrapProduct(Func<Product> getProduct, Action<Product> logCallback) { Box box = new Box(); Product prod = getProduct.Invoke(); // 这种情况就是模板方法:其他逻辑已经确定,其中有一个步骤依赖委托的结果,一般出现在代码的中间部分 if (logCallback != null && prod.Price > 50) { logCallback(prod); // 这里就是回调方法:一般位于代码的最后部分,根据一定条件决定委托是否被触发调用 } box.Prod = prod; return box; } } class ProductFactory { public Product MakePizza() { Product prod = new Product() { Name = "Pizza", Price = 12.0 }; return prod; } public Product MakeToyCar() { Product prod = new Product() { Name = "Toy Car", Price = 100.0 }; return prod; } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; // Demo: 使用不当会使可读性下降、debug难度增加 namespace Delegate1 { class BadDelegate { // 使用Operation public static void UseOperation() { Operation op1 = new Operation(); Operation op2 = new Operation(); Operation op3 = new Operation(); // 形成一个操作链 op3.InnerOperation = op2; op2.InnerOperation = op1; op3.Operate(new object(), null, null); // 问题1:如果传入的2个Action为null,失败和成功的效果是什么?答:内层的操作会调用外层的回调! // 问题2:如果传入的2个Action不为null,会出现什么情况?答:所有默认callback会被穿透性屏蔽 } } class Operation { public Action DefaultSuccessCallback { get; set; } // 默认的,操作成功执行的回调 public Action DefaultFailureCallback { get; set; } public Operation InnerOperation { get; set; } // !!! public object Operate(object input, Action successCallback, Action failureCallback) { if (successCallback == null) { successCallback = this.DefaultSuccessCallback; } if (failureCallback == null) { failureCallback = this.DefaultFailureCallback; } object result = null; try { result = this.InnerOperation.Operate(input, successCallback, failureCallback); // !!! // Do something later } catch { failureCallback.Invoke(); } successCallback.Invoke(); return result; } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Threading; /* 委托高级使用: 多播委托 异步调用 */ namespace Delegate2 { class Program { static void Main(string[] args) { Work w = new Work(); #if false // 多播 Console.WriteLine("------------------------多播委托--------------------------"); Action act = new Action(w.DoWork1); act += new Action(w.DoWork2); act += new Action(w.DoWork3); act.Invoke(); #endif #if false // 委托的异步调用(隐式异步调用,自动在新的线程里面执行) Console.WriteLine("------------------------委托异步调用--------------------------"); Action actRed = new Action(w.DoWork1); Action actGreen = new Action(w.DoWork2); Action actBlue = new Action(w.DoWork3); actRed.BeginInvoke(null, null); // 第一个参数标识委托执行完成后的回调,第二个参数表示传递给回调的参数 actGreen.BeginInvoke(null, null); actBlue.BeginInvoke(null, null); #endif #if false // 显式异步调用,即显式创建线程 Thread th1 = new Thread(w.DoWork1); Thread th2 = new Thread(w.DoWork2); Thread th3 = new Thread(w.DoWork3); th1.Start(); th2.Start(); th3.Start(); Task task1 = new Task(new Action(w.DoWork1)); Task task2 = new Task(new Action(w.DoWork2)); Task task3 = new Task(new Action(w.DoWork3)); task1.Start(); task2.Start(); task3.Start(); #endif #if false // 使用接口取代委托 Console.WriteLine("-------------------------接口取代委托实现(多态原理)-------------------------"); IProductFactory pizzaFac = new PizzaFactory(); IProductFactory toycarFac = new ToyCarFactory(); WrapFactory wrap = new WrapFactory(); Box box1 = wrap.WrapProduct(pizzaFac); Box box2 = wrap.WrapProduct(toycarFac); Console.WriteLine("Product1:{0}", box1.Prod.Name); Console.WriteLine("Product2:{0}", box2.Prod.Name); #endif Console.ReadLine(); } } // 多播 class Work { public void DoWork1() { for (int i = 0; i < 5; ++i) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("---Red Work {0}---", i); Thread.Sleep(500); } } public void DoWork2() { for (int i = 0; i < 5; ++i) { Console.ForegroundColor = ConsoleColor.Green; Console.WriteLine("---Green Work {0}---", i + 1); Thread.Sleep(500); } } public void DoWork3() { for (int i = 0; i < 5; ++i) { Console.ForegroundColor = ConsoleColor.Blue; Console.WriteLine("---Blue Work {0}---", i); Thread.Sleep(500); } } } // *******************************************使用接口来取代委托*********************************************** interface IProductFactory { Product Make(); } class PizzaFactory : IProductFactory { public Product Make() { Product product = new Product(); product.Name = "Pizza"; return product; } } class ToyCarFactory : IProductFactory { public Product Make() { Product product = new Product(); product.Name = "Toy Car"; return product; } } class Product { public string Name { get; set; } } class Box { public Product Prod { get; set; } } class WrapFactory { public Box WrapProduct(IProductFactory prodFactory) { Box box = new Box(); Product product = prodFactory.Make(); // 多态 box.Prod = product; return box; } } }