参考资源
刘铁猛老师《C#入门详解》
1 什么是委托?
类似于C和C++中的函数指针,实现间接调用函数的功能。委托是函数指针的“升级版”
1.1 函数指针的示例
#include <iostream>
using namespace std;
typedef int(*Calc)(int a, int b);
int Add(int a, int b)
{
int result = a + b;
return result;
}
int Sub(int a, int b)
{
int result = a - b;
return result;
}
int main()
{
int x = 100;
int y = 200;
int z = 0;
Calc funcPoint1 = &Add;
Calc funcPoint2 = ⋐
cout << "Call directly with the function name:" << endl;
z = Add(x, y);
cout << "x + y = " << z << endl;
z = Sub(x, y);
cout << "x - y = " << z << endl;
cout << "Call indirectly using function Pointers:" << endl;
z = funcPoint1(x, y);
cout << "x + y = " << z << endl;
z = funcPoint2(x, y);
cout << "x - y = " << z << endl;
输出结果:
Call directly with the function name:
x + y = 300
x - y = -100
Call indirectly using function Pointers:
x + y = 300
x - y = -100
1.2 委托的示例
C#中常见的两种委托:
- action:接收没有返回值的委托
- func:接收有返回值的委托
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DelegateExample
{
internal class Program
{
static void Main(string[] args)
{
Calculator calculator = new Calculator();
Action action = new Action(calculator.Report);
calculator.Report();
action.Invoke();
action();
Func<int, int, int> func1 = new Func<int, int, int> (calculator.Add);
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);
Console.WriteLine(z);
z = func2.Invoke(x, y);
Console.WriteLine(z);
Console.ReadKey();
}
}
internal 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;
}
}
}
输出结果
I have 3 methods.
I have 3 methods.
I have 3 methods.
300
-100
2 委托的声明(自定义委托)
-
委托是一种类
-
委托声明方式是仿照C和C++函数指针的方式
-
注意声明的位置
- 不要写错位置变成嵌套类
-
委托和所封装的方法必须“类型兼容”
- 返回值的数据类型一致
- 参数列表在个数和数据类型上一致(参数名不需要一样)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DelegateExample
{
public delegate double Calc(double x, double y);
internal 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();
}
}
internal class Calculator
{
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;
}
}
}
输出结果
300
-100
20000
0.5
3 委托的一般使用
3.1 实例
3.1.1 正确使用1:模板方法 ,“借用”指定的外部方法来产生结果
using System;
using System.Collections.Generic;
namespace DelegateExample
{
internal 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;
}
}
}
输出结果
Pizza
Toy Car
3.1.2 正确使用2:回调方法,调用指定的外部方法
using System;
using System.Collections.Generic;
namespace DelegateExample
{
internal 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();
box.Product = product;
if (product.Price >= 50)
{
logCallback(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;
}
}
}
输出结果
Product Toy Car created at 2024/8/14 3:18:42. Price is 100.
Pizza
Toy Car
3.2 注意:慎重使用委托
- 方法级别的耦合,现实工作中慎重使用
- 使得可读性下降、debug的难度增加
- 把委托回调、异步调用和多线程纠缠在一起,会让代码变得难以阅读和维护
- 委托使用不当有可能造成内存泄漏和程序性能下降
4 委托的高级使用
4.1 多播委托
将多个委托合并起来
using System;
using System.Collections.Generic;
using System.Threading;
namespace MulticastDelegateExample
{
internal 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);
// Multicast delegate
action1 += action2;
action1 += action3;
action1.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} doing homework {1} hour(s).", this.ID, i);
Thread.Sleep(1000);
}
}
}
}
}
输出结果
Student 1 doing homework 0 hour(s).
Student 1 doing homework 1 hour(s).
Student 1 doing homework 2 hour(s).
Student 1 doing homework 3 hour(s).
Student 1 doing homework 4 hour(s).
Student 2 doing homework 0 hour(s).
Student 2 doing homework 1 hour(s).
Student 2 doing homework 2 hour(s).
Student 2 doing homework 3 hour(s).
Student 2 doing homework 4 hour(s).
Student 3 doing homework 0 hour(s).
Student 3 doing homework 1 hour(s).
Student 3 doing homework 2 hour(s).
Student 3 doing homework 3 hour(s).
Student 3 doing homework 4 hour(s).
4.2 隐式异步调用
4.2.1 同步和异步
串行=同步=单线程,并行=异步=多线程
4.2.2 隐式多线程和显式多线程
1. 直接同步调用:使用方法名
using System;
using System.Collections.Generic;
using System.Threading;
namespace MulticastDelegateExample
{
internal 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 };
stu1.DoHomework();
stu2.DoHomework();
stu3.DoHomework();
for (int i = 0; i < 10; i++)
{
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine("Main thread {0}.", i);
Thread.Sleep(1000);
}
}
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} doing homework {1} hour(s).", this.ID, i);
Thread.Sleep(1000);
}
}
}
}
}
输出结果
Student 1 doing homework 0 hour(s).
Student 1 doing homework 1 hour(s).
Student 1 doing homework 2 hour(s).
Student 1 doing homework 3 hour(s).
Student 1 doing homework 4 hour(s).
Student 2 doing homework 0 hour(s).
Student 2 doing homework 1 hour(s).
Student 2 doing homework 2 hour(s).
Student 2 doing homework 3 hour(s).
Student 2 doing homework 4 hour(s).
Student 3 doing homework 0 hour(s).
Student 3 doing homework 1 hour(s).
Student 3 doing homework 2 hour(s).
Student 3 doing homework 3 hour(s).
Student 3 doing homework 4 hour(s).
Main thread 0.
Main thread 1.
Main thread 2.
Main thread 3.
Main thread 4.
Main thread 5.
Main thread 6.
Main thread 7.
Main thread 8.
Main thread 9.
2. 间接同步调用:使用单播/多播委托
using System;
using System.Collections.Generic;
using System.Threading;
namespace MulticastDelegateExample
{
internal 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();
// action1 += action2;
// action1 += action3;
// action1.Invoke();
for (int i = 0; i < 10; i++)
{
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine("Main thread {0}.", i);
Thread.Sleep(1000);
}
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} doing homework {1} hour(s).", this.ID, i);
Thread.Sleep(1000);
}
}
}
}
}
输出结果
Student 1 doing homework 0 hour(s).
Student 1 doing homework 1 hour(s).
Student 1 doing homework 2 hour(s).
Student 1 doing homework 3 hour(s).
Student 1 doing homework 4 hour(s).
Student 2 doing homework 0 hour(s).
Student 2 doing homework 1 hour(s).
Student 2 doing homework 2 hour(s).
Student 2 doing homework 3 hour(s).
Student 2 doing homework 4 hour(s).
Student 3 doing homework 0 hour(s).
Student 3 doing homework 1 hour(s).
Student 3 doing homework 2 hour(s).
Student 3 doing homework 3 hour(s).
Student 3 doing homework 4 hour(s).
Main thread 0.
Main thread 1.
Main thread 2.
Main thread 3.
Main thread 4.
Main thread 5.
Main thread 6.
Main thread 7.
Main thread 8.
Main thread 9.
3. 隐式同步调用:使用委托的BeginInvoke
using System;
using System.Collections.Generic;
using System.Threading;
namespace MulticastDelegateExample
{
internal 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.BeginInvoke(null, null);
action2.BeginInvoke(null, null);
action3.BeginInvoke(null, null);
for (int i = 0; i < 10; i++)
{
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine("Main thread {0}.", i);
Thread.Sleep(1000);
}
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} doing homework {1} hour(s).", this.ID, i);
Thread.Sleep(1000);
}
}
}
}
}
输出结果
Main thread 0.
Student 3 doing homework 0 hour(s).
Student 1 doing homework 0 hour(s).
Student 2 doing homework 0 hour(s).
Main thread 1.
Student 2 doing homework 1 hour(s).
Student 3 doing homework 1 hour(s).
Student 1 doing homework 1 hour(s).
Student 1 doing homework 2 hour(s).
Student 3 doing homework 2 hour(s).
Student 2 doing homework 2 hour(s).
Main thread 2.
Student 2 doing homework 3 hour(s).
Student 3 doing homework 3 hour(s).
Student 1 doing homework 3 hour(s).
Main thread 3.
Student 2 doing homework 4 hour(s).
Student 3 doing homework 4 hour(s).
Main thread 4.
Student 1 doing homework 4 hour(s).
Main thread 5.
Main thread 6.
Main thread 7.
Main thread 8.
Main thread 9.
4. 显示同步调用:使用Thread或Task
using System;
using System.Collections.Generic;
using System.Threading;
namespace MulticastDelegateExample
{
internal 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 };
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();
for (int i = 0; i < 10; i++)
{
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine("Main thread {0}.", i);
Thread.Sleep(1000);
}
}
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} doing homework {1} hour(s).", this.ID, i);
Thread.Sleep(1000);
}
}
}
}
}
输出结果
Student 2 doing homework 0 hour(s).
Student 3 doing homework 0 hour(s).
Student 1 doing homework 0 hour(s).
Main thread 0.
Student 3 doing homework 1 hour(s).
Student 2 doing homework 1 hour(s).
Student 1 doing homework 1 hour(s).
Main thread 1.
Student 3 doing homework 2 hour(s).
Student 1 doing homework 2 hour(s).
Student 2 doing homework 2 hour(s).
Main thread 2.
Student 3 doing homework 3 hour(s).
Student 1 doing homework 3 hour(s).
Main thread 3.
Student 2 doing homework 3 hour(s).
Student 2 doing homework 4 hour(s).
Student 3 doing homework 4 hour(s).
Student 1 doing homework 4 hour(s).
Main thread 4.
Main thread 5.
Main thread 6.
Main thread 7.
Main thread 8.
Main thread 9.
4.2.3 应该适时地使用接口取代一些对委托的使用
using System;
using System.Collections.Generic;
namespace DelegateExample
{
internal 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 = "Toy Car";
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;
}
}
}
输出结果
Pizza
Toy Car