目录
1、什么是委托
- 委托(delegate)是函数指针的升级版
实例C/C++中的函数指针
一切皆地址
- 变量(数据)是以某个地址为起点的一段内存中所存储的值。
- 函数(算法)是以某个地址为起点的一段内存中所存储的一组机器语言指令。
直接调用和间接调用
- 直接调用:通过函数名来调用函数,CPU通过函数名直接获得函数所在地址并开始执行->返回
- 间接调用:通过函数指针来调用函数,CPU通过读取函数指针存储的值获得函数所在地址并开始执行->返回
委托的简单使用
Action委托、Func委托
委托的声明(自定义委托)
委托是一种类,类是数据类型所以委托也是数据类型
它的声明方法与一般的类不同,主要是为了照顾可读性
注意声明委托的位置
委托与封装的方法必须类型兼容(返回值的数据类型一致,参数列表在个数和数据类型上一致【参数名不需要一样】)
2、委托的一般使用
实例:把方法当作参数传递给另一个方法
正确使用1:模板方法,借用指定的外部方法来产生结果
- 相当于填空题
- 常位于代码中部
- 委托有返回值
正确使用2:回调(callback)方法,调用指定的外部方法
- 相当于流水线
- 常位于代码尾部
- 委托无返回值
注意:难精通+易使用+功能强大东西,一旦被滥用后果非常严重
- 缺点1:这是一种方法级别的紧耦合,现实工作要谨慎
- 缺点2:使可读性下降,debug难度增加
- 缺点3:把委托回调、异步调用和多线程纠缠在一切,会让代码变得难以阅读和维护
- 缺点4:委托使用不当有可能造成内存泄漏和程序性能下降。
using System;
using System.IO;
namespace 委托
{
/// <summary>
/// 自己声明委托double类型
/// </summary>
public delegate double Calc(double x,double y);
class Program
{
/// <summary>
/// 定义另一个int委托类型
/// </summary>
public delegate int TestClass1(int x, int y);
static void Main(string[] args)
{
Console.WriteLine(DateTime.Now);
Console.OutputEncoding = Encoding.UTF8;
#region 调用CalcuLator
/*
CalcuLator calcuLator = new CalcuLator();
Action action = new Action(calcuLator.Report);
//直接调用
calcuLator.Report();
//间接调用
action.Invoke();
action();
//fUNC委托
Func<int, int, int> func1 = new Func<int, int, int>(calcuLator.Add);
Func<int, int, int> func2 = new Func<int, int, int>(calcuLator.Sub);
Func<int, int, int> func3 = new Func<int, int, int>(calcuLator.Mul);
Func<int, int, int> func4 = new Func<int, int, int>(calcuLator.Div);
int x = 100;
int y = 200;
int z = 0;
z = func1.Invoke(x,y);
Console.WriteLine(z);
z = func2.Invoke(x, y);
Console.WriteLine(z);
z = func3.Invoke(x, y);
Console.WriteLine(z);
z = func4.Invoke(x, y);
Console.WriteLine(z);
*/
#endregion
#region 使用委托
CalcuLator1 calcuLator1 = new CalcuLator1();
Calc calc1 = new Calc(calcuLator1.Add);
Calc calc2 = new Calc(calcuLator1.Sub);
Calc calc3 = new Calc(calcuLator1.Mul);
Calc calc4 = new Calc(calcuLator1.Div);
//间接调用
double a = 100;
double b = 200;
double c = 0;
c = calc1.Invoke(a, b);
Console.WriteLine(c);
c = calc2.Invoke(a, b);
Console.WriteLine(c);
c = calc3.Invoke(a, b);
Console.WriteLine(c);
c = calc4.Invoke(a, b);
Console.WriteLine(c);
#endregion
#region 使用模板方法
/*
ProductFactory productFactory = new ProductFactory();
//包装工厂的实例
WrapFactory wrapFactory = new WrapFactory();
Func<Product> func1 = new Func<Product>(productFactory.MakePizza);//方法一生成pizza
Func<Product> func2 = new Func<Product>(productFactory.MakeToyCar);//方法二生成小汽车
//声明logcallback的实例
Logger logger = new Logger();
Action<Product> log = new Action<Product>(logger.log);
//传进模板方法
//开始调用模板方法
Box box1= wrapFactory.WrapPdoduct(func1,log);
Box box2 = wrapFactory.WrapPdoduct(func2,log);
Console.WriteLine(box1.Product.Name);
Console.WriteLine(box2.Product.Name);
*/
#endregion
#region 使用回调方法
#endregion
#region TestClass
//初始化实例
TestClass ts = new TestClass();
//创建委托类型的实例mn,并绑定到Addd()方法
TestClass1 mn1 = ts.Addd;
//创建委托类型的实例mn,并绑定到Subb()方法
TestClass1 mn2 = ts.Subb;
//委托的调用
int s1 = mn1(5,2);
int s2 = mn2(6, 3);
Console.WriteLine("运算结果是:"+s1.ToString());
Console.WriteLine("运算结果是:" + s2.ToString());
#endregion
#region 委托打印字符串到控制台和文件
//初始化实例
PrintString.printString ps1 = new PrintString.printString(PrintString.WriteToFile);
PrintString.printString ps2 = new PrintString.printString(PrintString.WriteToFile);
//调用
PrintString.sendString(ps1);
PrintString.sendString(ps2);
#endregion
Console.ReadKey();
}
}
#region CalcuLator类
/// <summary>
/// CalcuLator类
/// </summary>
class CalcuLator
{
public void Report()
{
Console.WriteLine("I Have 3 Methods!");
}
public int Add(int a , int b)
{
int result = a + b;
return result;
}
public int Sub(int a, int b)
{
int result = a - b;
return result;
}
public int Mul(int a, int b)
{
int result = a * b;
return result;
}
public int Div(int a, int b)
{
int result = a / b;
return result;
}
}
#endregion
#region CalcuLator1类四个方法加减乘除
class CalcuLator1
{
public double Add(double x,double y )
{
return x +y;
}
public double Sub(double x, double y)
{
return x - y;
}
public double Mul(double x, double y)
{
return x * y;
}
public double Div(double x, double y)
{
return x / y;
}
}
#endregion
#region 使用回调方法
//记录使用程序运行状态
//以回调方法传递到模板方法里
class Logger
{
public void log(Product product)
{
Console.WriteLine("Product'{0}' created at '{1}'.Price is {2}",product.Name,DateTime.UtcNow,product.price);
}
}
//模板方法
//产品类
class Product
{
//产品名称
public string Name { get; set; }
//产品价格
public double price { get; set; }
}
//包装箱
class Box
{
public Product Product { get; set; }
}
//负责包上盒子交给用户
class WrapFactory
{
public Box WrapPdoduct(Func<Product>getProduct,Action<Product>logCallback)
{
//定义模板方法
Box box = new Box();
Product product = getProduct.Invoke(); //调用委托
//添加是否调用回调方法
//如果大于50块钱就log一下
if (product.price>=50)
{
logCallback(product);
}
box.Product = product;
return box;
}
}
//生成产品
class ProductFactory
{
//生成披萨
public Product MakePizza()
{
Product product = new Product();
product.Name = "Pizza";
product.price = 12;
return product;
}
//生成玩具小汽车
public Product MakeToyCar()
{
Product product = new Product();
product.Name = "Car";
product.price = 100;
return product;
}
}
#endregion
#region TestClass测试委托
class TestClass
{
public int Addd(int x, int y)
{
int result = x + y;
return result;
}
public int Subb(int x, int y)
{
int result = x - y;
return result;
}
}
#endregion
#region 委托打印字符串到控制台
class PrintString
{
static FileStream fs;
static StreamWriter sw;
//委托声明
public delegate void printString(string s);
/// <summary>
/// 该方法将字符串打印到控制台
/// </summary>
/// <param name="str"></param>
public static void WriteToCon(string str)
{
Console.WriteLine("The String is :{0}",str);
}
/// <summary>
/// 该方法将字符串打印到文件
/// </summary>
/// <param name="s"></param>
public static void WriteToFile(string s)
{
fs = new FileStream("d:\\test.txt",FileMode.Append,FileAccess.Write);
sw = new StreamWriter(fs);
sw.WriteLine(s);
sw.Flush();
sw.Close();
fs.Close();
}
/// <summary>
///该方法把委托作为参数,并使用它调用方法
/// </summary>
/// <param name="printString"></param>
public static void sendString(printString printString)
{
printString("Hello LIANGZAI");
}
}
#endregion
}
3、多播(multicast)委托
隐式异步调用
同步与异步的简介
- 同步:你做完我在你的基础上继续坐
- 异步:咱们两个同时做,同步进行
同步调用和异步调用的对比
- 每一个运行的程序是一个进程(process)
- 每个进程可以有一个或多个线程(thread)
- 同步调用是在同一线程内
- 异步调用的底层机理是多线程
- 串行==同步==单线程,并行==异步==多线程
隐式多线程和显示多线程
- 直接同步调用:使用方法名
- 间接同步调用:使用单播/多播委托的invoke方法
- 隐式异步调用:使用委托的begininvoke
- 显示异步调用:使用Thread或Task
应该适时的使用接口(interface)取代一些对委托的使用
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Remoting.Messaging;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace 委托2
{
class Program
{
/// <summary>
/// 委托烧水实例
/// </summary>
public delegate void DoWorkHandler();
public delegate void EndInvoke();
static void Main(string[] args)
{
Console.OutputEncoding = Encoding.UTF8;
#region MyDelegateTest和委托实现函数指针
/*
//步骤2:创建delegate对象实例
MyDelegateTest.MyDelegate md = new MyDelegateTest.MyDelegate(MyDelegateTest.MyDelegateFunc);
//调用delegate对象
md("app1");
Console.WriteLine("***********************");
//调用Handler
Handler.EventHandler(1, 10, 20);
Handler.EventHandler(2, 20, 20);
*/
#endregion
#region 委托数组
/*
Operations[] operations =
{
MathOperation.Multip,
MathOperation.Square
};
for (int i = 0; i < operations.Length; i++)
{
Console.WriteLine("using operations[{0}]:", i);
oper.DisplayNumber(operations[i], 2.02);
oper.DisplayNumber(operations[i], 4);
}
*/
#endregion
#region 烧水实例同步进行
/*
Console.WriteLine("开始烧水了!");
//一直等到水烧开
DoWorkHandler handler = (() =>
{
//模拟一个耗时操作,例如把水烧开花了5秒钟。
Thread.Sleep(5000);
Console.WriteLine("水烧好了!");
});
//这时如果调用Invoke方法,由于Invoke是同步的,线程就会被阻塞,
//即:在水没有烧开之前我们无法进行其他任何操作。
handler.Invoke();
//在水烧开前一直会有阻塞
Console.WriteLine("打个游戏吧!!");
*/
#endregion
#region 烧水实例异步进行
/*
Console.WriteLine("开始烧水了!");
//一直等到水烧开
DoWorkHandler handler1 = (() =>
{
//模拟一个耗时操作,例如把水烧开花了5秒钟。
Thread.Sleep(5000);
Console.WriteLine("水烧好了!");
});
//这里没有阻塞,我们可以做点其他事情
IAsyncResult asyncResult = handler1.BeginInvoke(null, null);
while (!asyncResult.IsCompleted)
{
Console.WriteLine("水还没烧好,先干点别的事!");
Thread.Sleep(1000);
}
//水烧好了
handler1.EndInvoke(asyncResult);
*/
#endregion
#region 烧水回调方法CallBack
/*
Console.WriteLine("开始烧水了!");
//水正在烧
DoWorkHandler handler = (() =>
{
Thread.Sleep(5000);
Console.WriteLine("水烧好了!");
});
//异步回调
IAsyncResult asyncResult = handler.BeginInvoke(new AsyncCallback(CallBack), null);
while (!asyncResult.IsCompleted)
{
Console.WriteLine("水还没烧好,干点别的事吧!");
Thread.Sleep(1000);
}
*/
#endregion
#region Student多播委托
//给委托传递具体方法
Teacher.DoSomething doSomething = new Teacher.DoSomething(Student.MakeTea);
doSomething += Student.MakeTea;
//委托被赋予了具体方法
doSomething();
#endregion
#region 按照员工的工资进行排序(冒泡排序)
//类实例数组
Employee[] employee = {
new Employee("Bugs Bunny ",2500),
new Employee("Elmer Fudd ", 4200),
new Employee("Daffy Duck", 3700),
new Employee("Wile Coyote ", 7500),
new Employee("Any ener ", 6000)
};
BubbleSorter.Sort(employee, Employee.compareSalary);
//对排序好的资金进行遍历
foreach (var i in employee)
{
Console.WriteLine(i);
}
#endregion
#region 匿名委托
string mid = ",middle pat";
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"));
#endregion
Console.ReadKey();
}
#region 烧水回调方法CallBack
public static void CallBack(IAsyncResult result)
{
DoWorkHandler handler = (result as AsyncResult).AsyncDelegate as DoWorkHandler;
handler.EndInvoke(result);
}
#endregion
}
#region MyDelegateTest
class MyDelegateTest
{
//步驟1:声明delegate对象
public delegate void MyDelegate(string name);
//这是我们想要传递的方法,需要和MyDelegate有相同的参数和返回值类型
public static void MyDelegateFunc(string name)
{
Console.WriteLine("Hello,", name);
}
}
#endregion
#region 委托实现函数指针
class MathClass
{
public static int max(int a,int b)
{
return (a > b ? a : b);
}
public static int min(int a, int b)
{
return (a < b ? a : b);
}
public static int sub(int a, int b)
{
return (a+b);
}
public static int minus(int a, int b)
{
return (a-b);
}
}
class Handler
{
private delegate int Calcl(int a,int b);
private static Calcl[] myCalcl = new Calcl[2];
public static void EventHandler(int i ,int a, int b)
{
switch (i)
{
case 1:
myCalcl[0] = new Calcl(MathClass.max);//最大值
myCalcl[1] = new Calcl(MathClass.min);//最小值
Console.WriteLine(myCalcl[0](a,b));
Console.WriteLine(myCalcl[1](a,b));
break;
case 2:
myCalcl[0] = new Calcl(MathClass.sub);//和
myCalcl[1] = new Calcl(MathClass.minus);//差
Console.WriteLine(myCalcl[0](a,b));
Console.WriteLine(myCalcl[1](a,b));
break;
default:
return;
}
}
}
#endregion
#region 委托数组
/// <summary>
/// 该数组的元素初始化为MathOperations类的不同操作,遍历这个数组,可以将每个操作应用到2个不同的值中。
/// 这种用法的好处是,可以在循环中调用不同的方法。
/// </summary>
/// <param name="x"></param>
/// <returns></returns>
delegate double Operations(double x);
class oper
{
public static void DisplayNumber(Operations action,double value)
{
double result = action(value);
Console.WriteLine("input value is {0},result of operation {1}", value,result);
}
}
struct MathOperation
{
public static double Multip(double value)
{
return value * 2;
}
public static double Square(double value)
{
return value * value;
}
}
#endregion
#region Student多播委托
class Student
{
public static void MakeTea()
{
Console.WriteLine("Got it!");
}
public static void BuyPen()
{
Console.WriteLine(" On my way!");
}
}
class Teacher
{
//声明一个委托
public delegate void DoSomething();
}
#endregion
#region 按照员工工资冒泡排序
class BubbleSorter
{
static public void Sort<T>(IList<T> sortArray, Func<T, T, bool> comparison)
{
bool swapped = true;
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;
}
}
} while (swapped);
}
}
class Employee
{
public Employee(string name,decimal salary)
{
this.Name = name;
this.Salary = salary;
}
public string Name { get; set; }
public decimal Salary { get; set; }
public override string ToString()
{
return string.Format("{0},{1:C}", Name, Salary);
}
public static bool compareSalary(Employee e1,Employee e2)
{
return e1.Salary < e2.Salary;
}
}
#endregion
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
namespace 事件
{
class Program
{
static void Main(string[] args)
{
#region 委托高级应用单播、多播、隐式在、显示
//使用委托调用homework方法
Student stu1 = new Student() { ID = 1, PenColor = ConsoleColor.Yellow };
Student stu2 = new Student() { ID = 2, PenColor = ConsoleColor.Green };
Student stu3 = new Student() { ID = 3, PenColor = ConsoleColor.Blue };
Student stu4 = new Student() { ID = 4, PenColor = ConsoleColor.DarkMagenta };
//使用Action委托
Action action1 = new Action(stu1.DoHomework);
Action action2 = new Action(stu2.DoHomework);
Action action3 = new Action(stu3.DoHomework);
Action action4 = new Action(stu4.DoHomework);
//单播委托
/*
action1.Invoke();
action2.Invoke();
action3.Invoke();
action4.Invoke();
*/
//多播委托把2.3.4使用拉姆达表达式合并到1
/*
action1 += action2;
action1 += action3;
action1 += action4;
//只调用action1看效果
action1.Invoke();
*/
/*
//第一种同步调用、直接同步调用,直接调用方法名
stu1.DoHomework();
stu2.DoHomework();
stu3.DoHomework();
stu4.DoHomework();
//主线程还有事情要做
for (int i = 0; i < 10; i++)
{
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine("Main thread {0}",i);
Thread.Sleep(1000);
}
*/
//第二种使用单播委托进行——间接同步调用
/*
action1.Invoke();
action2.Invoke();
action3.Invoke();
action4.Invoke();
for (int i = 0; i < 10; i++)
{
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine("Main thread {0}", i);
Thread.Sleep(1000);
}
*/
//第三种多播委托也是间接同步调用
/*
action1 += action2;
action1 += action3;
action1 += action4;
action1.Invoke();
for (int i = 0; i < 10; i++)
{
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine("Main thread {0}", i);
Thread.Sleep(1000);
}
*/
//使用BeginInvoke委托进行隐式异步调用——回调方法,不需要回调是填null
/*
action1.BeginInvoke(null,null);
action2.BeginInvoke(null, null);
action3.BeginInvoke(null, null);
action4.BeginInvoke(null, null);
for (int i = 0; i < 10; i++)
{
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine("Main thread {0}",i);
Thread.Sleep(1000);
}
*/
//显示的异步调用
//1、古老的方式
/*
Thread thread1 = new Thread(new ThreadStart(stu1.DoHomework));
Thread thread2 = new Thread(new ThreadStart(stu2.DoHomework));
Thread thread3= new Thread(new ThreadStart(stu3.DoHomework));
Thread thread4 = new Thread(new ThreadStart(stu4.DoHomework));
thread1.Start();
thread2.Start();
thread3.Start();
thread4.Start();
for (int i = 0; i < 10; i++)
{
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine("Main thread {0}", i);
Thread.Sleep(1000);
}
*/
//2、使用Task都会发生资源争抢
/*
Task task1 = new Task(new Action(stu1.DoHomework));
Task task2 = new Task(new Action(stu2.DoHomework));
Task task3 = new Task(new Action(stu3.DoHomework));
Task task4 = new Task(new Action(stu4.DoHomework));
task1.Start();
task2.Start();
task3.Start();
task4.Start();
*/
#endregion
#region 简单接口的认识
/*
IProductFactory pizzaFactory = new PizzaFactory();
IProductFactory toycarFactory = new ToyCarFactory();
WrapFactory wrapFactory = new WrapFactory();
Box box1 = wrapFactory.WrapPdoduct(pizzaFactory);
Box box2 = wrapFactory.WrapPdoduct(toycarFactory);
Console.WriteLine(box1.Product.Name);
Console.WriteLine(box2.Product.Name);
*/
#endregion
#region 简单事件的认识
//初始化实例
Incrementer incrementer = new Incrementer();
Dozens dozens = new Dozens(incrementer);
SomeOtherClass someOtherClass = new SomeOtherClass(incrementer);
incrementer.DoCount();
#endregion
Console.ReadKey();
}
}
#region 委托高级应用单播、多播、隐式在、显示
class Student
{
/// <summary>
/// 学生ID
/// </summary>
public int ID
{
get;
set;
}
/// <summary>
/// 学生使用笔的颜色
/// </summary>
public ConsoleColor PenColor
{
get;
set;
}
/// <summary>
/// 实例方法做作业
/// </summary>
public void DoHomework()
{
for (int i = 0; i < 5; i++)
{
Console.ForegroundColor = this.PenColor;
Console.WriteLine("Student {0} doing homework {1} hours", this.ID, i);
//调用线程1000毫秒,也就是1秒钟
Thread.Sleep(1000);
}
}
}
#endregion
#region 简单接口的认识
interface IProductFactory
{
Product Make();
}
/// <summary>
/// 重构购买
/// </summary>
class PizzaFactory : IProductFactory
{
public Product Make()
{
Product product = new Product();
product.Name = "Pizza";
product.price = 12;
return product;
}
}
class ToyCarFactory : IProductFactory
{
public Product Make()
{
Product product = new Product();
product.Name = "Car";
product.price = 100;
return product;
}
}
class Product
{
public string Name { get; set; }
public double price { get; set; }
}
class Box
{
public Product Product { get; set; }
}
class WrapFactory
{
public Box WrapPdoduct(IProductFactory productFactory)
{
//定义模板方法
Box box = new Box();
Product product = productFactory.Make();
//添加是否调用回调方法
return box;
}
}
// 生成产品
/*
class ProductFactory
{
//生成披萨
public Product MakePizza()
{
Product product = new Product();
product.Name = "Pizza";
product.price = 12;
return product;
}
//生成玩具小汽车
public Product MakeToyCar()
{
Product product = new Product();
product.Name = "Car";
product.price = 100;
return product;
}
}
*/
#endregion
#region 简单事件的认识
delegate void Handler();
class Incrementer //发布者
{
public event Handler CountedADozen;
public void DoCount() //触发事件的方法
{
for (int i = 1; i < 50; i++)
{
//每增加12小时个计时器就触发事件一次
if (i%12==0&&CountedADozen !=null)
{
CountedADozen();
}
}
}
}
class Dozens
{
public Dozens(Incrementer incrementer)
{
//在发布私有委托里添加方法
incrementer.CountedADozen += IncrementDozensCount;
}
//事件成员被触发时调用的方法
void IncrementDozensCount()
{
Console.WriteLine("Dozens");
}
}
class SomeOtherClass
{
public SomeOtherClass(Incrementer incrementer)
{
//在发布私有委托里添加方法
incrementer.CountedADozen += DoSomething;
}
//事件成员被触发时调用的方法
public void DoSomething()
{
Console.WriteLine("SomeOtherClass");
}
}
#endregion
}