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;
}
}
}