C# 委托-《C#语言入门详解》学习笔记
跟着Timothy Liu老师学C# 系列
写在前面:
无意中找到了刘铁猛老师的C#视频,越听越带劲,手机都不想玩了,真的讲的融会贯通,非常带劲,让人把知识点串联的同时,还不得不感叹,真TM牛逼!老师本身是微软算法组高级工程师,所以实力不是一般的强。
能上youtube的同学可以去刘老师的主页 主页.观看。
一、委托的定义
C# 中的委托(Delegate)类似于 C 或 C++ 中函数的指针。委托(Delegate) 是存有对某个方法的引用的一种引用类型变量。引用可在运行时被改变。
委托(Delegate)特别用于实现事件和回调方法。所有的委托(Delegate)都派生自 System.Delegate 类。
声明了委托类型,委托对象必须使用 new 关键字来创建,且与一个特定的方法有关。当创建委托时,传递到 new 语句的参数就像方法调用一样书写,但是不带有参数。
二、如何委托?
1.利用C#准备好的委托Action 和Func进行委托,其中Action无参数且无返回值,Func可以带参数和返回值。
namespace DelegateTest
{
class Program
{
static void Main(string[] args)
{
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); //两个int参数,且有一个int返回值
Func<int, int, int> func2 = new Func<int, int, int>(calculator.Sub);
int x = 100;
int y = 200;
int z = 0;
z = func1.Invoke(x,y); //可简写为 z=func1(x,y)
Console.WriteLine(z);
z = func2.Invoke(x, y); //可简写为 z=func2(x,y)
Console.WriteLine(z);
Console.ReadKey();
}
}
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;
}
}
}
2. 自己声明委托,委托是一个类,使用关键字delegate。委托与所封装的方法必须“类型兼容”,即返回值的数据类型一致,参数列表在个数和数据类型上一致(参数名不需要一样)。
namespace DelegateTest
{
public delegate double Calc(double x, double y);
class Program
{
static void Main(string[] args)
{
Calculator calculator = new Calculator();
Calc calc1 = new Calc(calculator.Add);
Calc calc2 = new Calc(calculator.Sub);
Calc calc3 = new Calc(calculator.Mul);
Calc calc4 = new Calc(calculator.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);
Console.ReadKey();
}
}
class Calculator
{
public double Add(double a, double b)
{
return a + b;
}
public double Sub(double a, double b)
{
return a - b;
}
public double Mul(double a, double b)
{
return a * b;
}
public double Div(double a,double b)
{
return a / b;
}
}
}
三、委托的一般使用:
- 模板方法,“借用”指定的外部方法来产生结果
- 相当于“填空题”;
- 常位于代码中部;
- 委托有返回值;
namespace DelegateTest
{
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);
Console.WriteLine(box1.Product.Name);
Console.WriteLine(box2.Product.Name);
Console.ReadKey();
}
}
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;
}
}
}
- ==回调(callback)==方法,调用指定的外部方法
- 相当于“流水线”;
- 常位于代码末尾;
- 委托无返回值。
namespace DelegateTest
{
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);
Console.ReadKey();
}
}
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 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;
}
}
}
四、委托的高级使用
- 单播委托
namespace MulticastDelegageExample
{
class Program
{
static void Main(string[] args)
{
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(); //单播
Console.ReadKey();
}
}
class Student
{
public int ID { get; set; }
public ConsoleColor PenColor { get; set; }
public void DoHomework()
{
for (int i = 0; i < 5; i++)
{
Console.ForegroundColor = this.PenColor;
Console.WriteLine("Student {0} dong homework {1} hour(s).",this.ID,i);
Thread.Sleep(1000);
}
}
}
}

2. 多播(multicast)委托
//多播委托
action1 += action2;
action1 += action3;
action1.Invoke();
- 隐式异步调用
BeginInvoke自动创建分支线程,分支线程里调用封装的方法。
action1.BeginInvoke(null, null); //BeginInvoke自动创建分支线程,分支线程里调用封装的方法
action2.BeginInvoke(null, null);
action3.BeginInvoke(null, null);
但是3个线程都在访问控制台前景色属性,多个线程访问同一资源时,会争抢资源,发生冲突。

为了防止这种情况,可以给线程加锁。一般使用lock或者monitor,具体可参考 该文.
class Student
{
static object locker = new object(); //创建锁
public int ID { get; set; }
public ConsoleColor PenColor { get; set; }
public void DoHomework()
{
for (int i = 0; i < 5; i++)
{
lock (locker) //每次只能有1个线程进行操作
{
Console.ForegroundColor = this.PenColor;
Console.WriteLine("Student {0} dong homework {1} hour(s).", this.ID, i);
Thread.Sleep(1000);
}
}
}
}
结果如下:

4.显示异步,自己声明委托,调用有2种方法
- 多线程,也会发生资源争抢的情况
Thread thread1 = new Thread(new ThreadStart(stu1.DoHomework));
Thread thread2 = new Thread(new ThreadStart(stu2.DoHomework));
Thread thread3 = new Thread(new ThreadStart(stu3.DoHomework));
thread1.Start();
thread2.Start();
thread3.Start();
- 使用C#本身的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));
task1.Start();
task2.Start();
task3.Start();
- 应适时地使用接口(interface)取代一些对委托的使用

namespace DelegateExample
{
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);
Console.ReadKey();
}
}
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 = "ToyCar";
return product;
}
}
class Product
{
public string Name { get; set; }
}
class Box
{
public Product Product { get; set; }
}
class WrapFactory
{
public Box WrapProduct(IProductFactory productFactory)
{
Box box = new Box();
Product product = productFactory.Make();
box.Product = product;
return box;
}
}
}
本文深入讲解C#中的委托概念,包括定义、使用方法及高级应用。通过实例演示如何利用Action和Func进行委托,以及自定义委托的声明和使用。探讨委托在模板方法、回调和多播场景中的应用。

2092

被折叠的 条评论
为什么被折叠?



