《C#入门详解》刘老师 delegate
delegate
-
什么是委托(委托可以按照一定的约束指向某些目标方法,然后帮助我们完成对这些方法的间接调用)
例子:
namespace delegateExample { class program { static void main(string[] args) { Calculator calculctor = new Calculator(); Action action = new Action(calculator.report); //只需要方法名calculator.report, calculator.report() 加括号代表返回值 /*========这边定义了一个指针变量,把方法名赋给指针变量========读取函数指针存储的值获得函数所在地址=======又因为这个指针变量是委托,委托是一个类,所以这个指针变量以声明一个类来定义*/ calculator.report(); //直接调用 action.invoke(); //间接调用 action(); //简略写法
Func<int,int,int> func1 = new Func<int,int,int>(Calculator.Add) ;
int x=100;
int y=200;
int z=0;
z = func1.Invoke(x,y);
} } class Calculator { public void report(){console.writeline("i have method");} public int add(int a,int b){int result=a+b;return result;} } }
-
为什么使用委托
/// <summary> /// the English speaker. /// </summary> /// <param name="name">The name.</param> public void EnglishSpeaker(string name) { Console.WriteLine( string.Format("Hello my name is {0} and I am English speaker.\n", name)); } /// <summary> /// the Chineses speaker. /// </summary> public void ChineseSpeaker(string name) { Console.WriteLine( string.Format("我的名字叫{0},我讲普通话。\n", name)); }
/// 根据上下文调用不同的方法 /// </summary> /// <param name="name">string</param> /// <param name="fang">enum</param> private static void Say(string name, Language fang) { switch (fang) { case Language.Chinese: Program.ChineseSpeaker(name); break; case Language.English: Program.EnglishSpeaker(name); break; default : break; } }
问题:
如果我们现在又要增加新的语言西班牙语,同样我们可以增加西班牙语,但我们必须修改switch语句增加判断,这不符合OOP中的OCP(对扩展开放,对修改关闭原则),这时候委托该登场
答案:
首先我们定义了一种委托类型SpeakDelegate,然后我们通过修改Say方法看看该如何使用委托变量。
假如Say()方法可以接受一个参数变量,这个变量可以代表另一个方法,当我们给这个变量赋值 EnglishSpeaker的时候,它代表着EnglishSpeaker() 这个方法;
当我们给它赋值ChineseSpeaker 的时候,它又代表着ChineseSpeaker()方法。我们将这个参数变量命名为 LanguageSpeak,那么不是可以如同给name赋值时一样,
在调用 Say()方法的时候,给这个LanguageSpeak参数也赋上值么(ChineseSpeak或者EnglsihSpeak等)?然后,我们在方法体内,也可以像使用别的参数一样使用LanguageSpeak。
private delegate void SpeakDelegate(string name); private static void Say(string name, SpeakDelegate speaker) { ///Inoke the speaker function. speaker(name); }
-
自定义委托
namespace delegateExample { public delegate double Calc(double x,double y); class program { static void main(string[] args) { Calculator calculator = new Calculator(); Calc calc = new Calc(calculator.add); double a =100; double b=200; double c=0; c = calc.invoke(a,b); c = calc(a,b); console.writeline(c); } } class Calculator { public double add( double x, double y){ return x+y;} } }
-
委托的一般使用
1.委托的一般使用:把委托当作方法的参数传递到方法中去。Advantage:写了一个方法,这个方法有一个委托类型的参数,委托封装了一个方法。在方法体中使用传进来的这个委托间接调用委托封装了的方法,这样就形成了一种动态调用方法的代码结构
2.模板方法:写了一个方法,通过传进来的委托参数,“借用”指定的外部方法来产生结果。这就相当于在我写的方法中有一个“填空题”,“填空题”的空白之处就用传进来的委托类型的参数进行填补。也就是通过传进来的委托类型的参数间接的调用我指定的外部方法,这个方法一般有返回值。当我拿到这个返回值在继续执行我所写方法后面的逻辑。
3.回调方法:某个方法我可以调用它,用的着的时候使用,用不着的时候不使用,这就是回调方法。当以回调方法的形式来使用委托的时候,我们要做的是把委托类型的参数传进主调方法中去,被传进主调方法里的委托类型的参数的内部会封装着一个被回调的方法,即回调方法。主调函数会根据自己的逻辑来决定是不是调用这个回调方法。
/*==================模板方法====================*/ namespace delegate { class program { static void main(string[] args) { ProductFactory productFactory = new ProductFactory(); WrapFactory wrapFactory = new WrapFactory(); Func<Product> func1 = new Func<Product> (productFactory.MakePizza); Func<Product> func2 = new Func<Product> (productFactory.MakeToyCar); Box box1 = wrapFactory.WrapProduct(func1); Box box2 = wrapFactory.WrapProduct(func2);//Box box2 = wrapFactory.WrapProduct( new Func<Product> (productFactory.MakeToyCar) ) console.writeline(box1.Product.Name); console.writeline(box2.Product.Name); } } class Product { public string name{get;set;} } class Box { public Product product{get;set;} } class WrapFactory { public Box WrapProduct(Func<product> getproduct) { Box box = new Box(); Product product = getproduct.invoke(); box.Product = product; return box; } } class ProductFactory { public Product Makepizza() { Product product = new Product(); product.name = "pizza"; return product ; } public Product MakeToyCar() { Product product = new Product(); product.name = "Toy Car"; return product ; } } } 模板方法的好处: Product类,Box类,WrapFactory类,都不需要修改,只需要不停的扩展产品工厂,那么就可以生产不同不样的产品。而且不管生产哪种产品的方法。 只要把这个方法封装在一个委托类型的对象里,传给我们的模板方法。我们的模板方法就一定能够把我的这个产品包装成一个箱子并交还回来。最大限度的重复使用代码
/*==================回调方法====================*/ namespace delegate { class program { static void main(string[] args) { ProductFactory productFactory = new ProductFactory(); WrapFactory wrapFactory = new WrapFactory(); Func<Product> func1 = new Func<Product> (productFactory.MakePizza); Func<Product> func2 = new Func<Product> (productFactory.MakeToyCar); Logger logger = new Logger(); Action<Product> log = new Action<Product> (Logger.Log); Box box1 = wrapFactory.WrapProduct(func1 , log); Box box2 = wrapFactory.WrapProduct(func2 , log); console.writeline(box1.Product.Name); console.writeline(box2.Product.Name); } } class Logger { public void Log( Product product) {console.writeline("Product '{0}' created at '{1}' ,pizza is '{2}',product.name,datetime.now,product.price");} } class Product { public string name{get;set;} public string price{get;set;} } class Box { public Product product{get;set;} } class WrapFactory { public Box WrapProduct(Func<product> getproduct , Action <Product> logcallback) { Box box = new Box(); Product product = getproduct.invoke(); 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 = "Toy Car"; product.price = 100; return product ; } } }
-
委托的高级使用
/*=================多播委托====================*/ using system; using system.collection.generic; using system.threading; namespace MulticastDelegateExample { class program { 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.Red}; Action action1 = new Action(stu1.DoHomework); Action action2 = new Action(stu2.DoHomework); Action action3 = new Action(stu3.DoHomework); action1.invoke();//一个委托封装了一个方法的形式叫做单播委托 action2.invoke(); action3.invoke(); action1 += action2; action2 += action3; action1.invoke(); //一个委托封装了多个方法的使用方式叫做多播委托,并且这里执行action1.invoke(),上面stu1,2,3的dohomework方法都会执行
action1 -= action2; action2 -= action3;
action1 -= stu1.DoHomework;
action1?.invoke(); //这里action1委托什么方法都没有订阅,可能为空,所以加上 ”?“
} class Student { public int ID{get;set;} public ConsoleColor PenColor{get;set;} public void DoHomeWork() { console.ForegroundColor = this.PenColor; console.Writeline("student{0}doing homework{1}hours",this.ID, i); Thread.Sleep(1000); } } }
/*=============使用接口来代替委托======================= namespace delegate { class program { static void main(string[] args) { IproductFactory pizzaFactory = new PizzaFactory(); IproductFactory toycarFactory = new ToyCarFactory(); WrapFactory wrapFactory = new WrapFactory(); Box box1 = wrapFactory.WrapProduct( pizzaFactory ); Box box2 = wrapFactory.WrapProduct( toycarFactory ); console.writeline(box1.Product.Name); console.writeline(box2.Product.Name); } } 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 product{get;set;} } class WrapFactory { public Box WrapFactory(IproductFactory productFactory) { Box box = new Box(); Product product = productFactory.Make(); box.Product = product; return box; } } }
委托高级使用补充:
一、刘老师讲到的委托使用不当可能造成内存泄漏?
委托会引用着一个方法,如果这个方法是一个实例方法。这个方法隶属于一个对象,委托引用这个方法,那么这个对象必须存在于内存当中。即便没有其他的引用变量引用这个对象,这个对象的内存也不能被释放。
二、隐式异步调用(winform中一个按钮切换到另一个线程中,RP多线程)
1 /*=================使用委托进行隐式异步调用====================*/ 2 using system; 3 using system.collection.generic; 4 using system.threading; 5 6 namespace MulticastDelegateExample 7 { 8 class program 9 { 10 Student stu1 = new Student(){ID =1,PenColor = ConsoleColor.Yellow}; 11 Student stu2 = new Student(){ID =2,PenColor = ConsoleColor.Green}; 12 Student stu3 = new Student(){ID =3,PenColor = ConsoleColor.Red}; 13 14 Action action1 = new Action(stu1.DoHomework); 15 Action action2 = new Action(stu2.DoHomework); 16 Action action3 = new Action(stu3.DoHomework); 17 18 action1.BeginInvoke(null,null);//这个方法会自动的为我们生成一个分支线程,在分支线程中调用封装的方法,打上括号之后IAsyncRsult.Action.BeginInvoke(AsyncCallback callback,object @object) 19 //调用完这个方法后,你让我采取哪些行动,即回调方法21 26 28 action2.BeginInvoke(null,null);//这个方法会自动的为我们生成一个分支线程,在分支线程中调用封装的方法,打上括号之后IAsyncRsult.Action.BeginInvoke(AsyncCallback callback,object @object) 29 //调用完这个方法后,你让我采取哪些行动,即回调方法21 30 action3.BeginInvoke(null,null);//这个方法会自动的为我们生成一个分支线程,在分支线程中调用封装的方法,打上括号之后IAsyncRsult.Action.BeginInvoke(AsyncCallback callback,object @object) 31 //调用完这个方法后,你让我采取哪些行动,即回调方法21 27 } 28 29 class Student 30 { 31 public int ID{get;set;} 32 public ConsoleColor PenColor{get;set;} 33 public void DoHomeWork() 34 { 35 console.ForegroundColor = this.PenColor; 36 console.Writeline("student{0}doing homework{1}hours",this.ID, i); 37 Thread.Sleep(1000); 38 } 39 } 40 }
/*=================显示异步调用,两种方式,一种是thread====================*/ using system; using system.collection.generic; using system.threading; namespace MulticastDelegateExample { class program { 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.Red}; Thread thread1 = new Thread(new ThreadStart(stu1.DoHomework)) Thread thread2 = new Thread(new ThreadStart(stu2.DoHomework)) Thread thread3 = new Thread(new ThreadStart(stu3.DoHomework)) therad1.Start(); therad2.Start(); therad3.Start(); } class Student { public int ID{get;set;} public ConsoleColor PenColor{get;set;} public void DoHomeWork() { console.ForegroundColor = this.PenColor; console.Writeline("student{0}doing homework{1}hours",this.ID, i); Thread.Sleep(1000); } } }
/*=================显示异步调用两种方式,另一种是Task====================*/ using system; using system.collection.generic; using system.threading; namespace MulticastDelegateExample { class program { 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.Red}; Task task1 = new Task(new Action(stu1.DoHomework)); Task task2 = new Task(new Action(stu2.DoHomework)); Task task3 = new Task(new Action(stu3.DoHomework)); task1.Start(); task2.Start(); task3.Start(); } class Student { public int ID{get;set;} public ConsoleColor PenColor{get;set;} public void DoHomeWork() { console.ForegroundColor = this.PenColor; console.Writeline("student{0}doing homework{1}hours",this.ID, i); Thread.Sleep(1000); } } }