委托(delegate)是一種更新版的“函數指針”。
-
一切皆位址
變量(資料)是以某個位址為起點的一段記憶體中存儲的值。比如我們聲明了一個變量a,則cpu會找到變量a指向的記憶體首位址,根據a變量的配置設定大小,擷取一整塊屬于a的記憶體。
函數(算法)是以某個位址為起點的一段記憶體中存儲的機器語言指令。cpu會根據一條條的機器指令來完成我們的算法邏輯。
-
直接調用和間接調用
直接調用:通過函數名來調用函數,cpu根據函數名直接獲得該函數的所在位址并開始執行。執行完畢後傳回到調用者繼續向下執行。
間接調用:通過函數指針來調用函數,cpu根據函數指針存儲的值獲得函數所在位址并開始執行,執行完畢後傳回調用者繼續向下執行。
我們可以看出直接調用和間接調用其實是一緻的,都會擷取我們想調用的那個函數的所在位址。
-
強類型委托
Action委托 Func委托
這兩種簡單的委托為我們提供了基本的配置和限制~~ 以下為對這兩種委托适用的小例子:

using System;
namespace LanguageLearn
{
internal class Program
{
static void Main(string[] args)
{
var calculator = new Calculator();
var action = new Action(calculator.Report);
calculator.Report(); // 直接調用
action.Invoke(); // 間接調用
action(); // 函數指針調用形式
Func<int, int, int> funcAdd = new Func<int, int, int>(calculator.Add);
var addResult = funcAdd.Invoke(10, 20);
Console.WriteLine(addResult);
Func<int, int, int> funcSub = new Func<int, int, int>(calculator.Sub);
var subResult = funcSub.Invoke(10, 5);
Console.WriteLine(subResult);
Func<int, int, int> funcMuti = new Func<int, int, int>((x, y) => x * y);
var mutiResult = funcMuti.Invoke(10, 5);
Console.WriteLine(mutiResult);
}
}
internal class Calculator
{
public void Report()
{
Console.WriteLine("I am a calculator");
}
public int Add(int a, int b)
{
return a + b;
}
public int Sub(int a, int b)
{
return a - b;
}
}
}
//out put
//I am a calculator
//I am a calculator
//I am a calculator
//30
//5
//50
delegate
-
委托的聲明
委托是一種資料類型,并且它是一種類,是以它是引用類型,我把它了解成一種特别的引用類型,它是對函數方法類型的一種引用。
它的聲明方式與類的聲明不用,關鍵字為delegate。
注意聲明委托的位置,它不應成為嵌套類型。
委托與所委托的方法必須“類型相容”,傳回值需一緻,入參的參數類型和參數個數也需要一緻。

using System;
namespace LanguageLearn
{
internal delegate double CalDelegate(double x, double y);
internal class Program
{
static void Main(string[] args)
{
var calculator = new Calculator();
CalDelegate calDelegate1 = new CalDelegate(calculator.Add);
CalDelegate calDelegate2 = new CalDelegate(calculator.Sub);
CalDelegate calDelegate3 = new CalDelegate(calculator.Mul);
CalDelegate calDelegate4 = new CalDelegate(calculator.Div);
CalDelegate calDelegate5 = new CalDelegate((a, b) => a * b + 1);
double a = 100;
double b = 200;
double c = 0;
c = calDelegate1.Invoke(a, b);
Console.WriteLine(c);
c = calDelegate2.Invoke(a, b);
Console.WriteLine(c);
c = calDelegate3.Invoke(a, b);
Console.WriteLine(c);
c = calDelegate4.Invoke(a, b);
Console.WriteLine(c);
c = calDelegate5.Invoke(a, b);
Console.WriteLine(c);
}
}
internal class Calculator
{
public void Report()
{
Console.WriteLine("I am a 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;
}
}
}
custom delegate
-
委托的一般使用
執行個體:将方法當作參數傳遞給另一個方法
解釋:我們在方法體的内部,适用參數傳遞進來的委托,間接的調用封裝在委托内的方法。形成了一種動态調用方法的結構。
正确使用1:模闆方法,“借用” 指定的外部方法來産生結果 (在方法體中有一部分的空缺需要根據你傳遞的方法來完成)
-
-
-
- 相當于“填空題”
- 常位于代碼的中部
- 常具有傳回值
-
-
正确使用2:回調(callback)方法,調用指定的外部方法 (類似于“提供商”,當你需要哪種服務的時候,可以根據不同的“提供商”來調用你需要的委托方法)
-
-
-
- 相當于“流水線” (我們在主調方法中,根據主調方法的條件來決定是否調用委托來回調委托封裝的方法)
- 常位于代碼的尾部
- 委托無傳回值
-
-
模闆方法執行個體:利用方法委托參數,我們可以在不改變某些核心邏輯代碼的情況下,利用委托作為模闆,我們可以調用不同的方法,以達到不同的效果,在我了解,這是除了多态或工廠模式的另外一種減小了耦合度,遵循了開閉原則,的一種代碼高複用的實作形式。

using System;
using System.Text.Json;
namespace LanguageLearn
{
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(JsonSerializer.Serialize<Box>(box1));
Console.WriteLine(JsonSerializer.Serialize<Box>(box2));
}
}
class Product
{
public string Name { get; set; }
}
class Box
{
public Product Product { get; set; }
}
class WrapFactory
{
public Box WrapProduct(Func<Product> GetProduct)
{
var box = new Box();
box.Product = GetProduct.Invoke();
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 = "ToyCar";
return product;
}
}
}
// out put
//{ "Product":{ "Name":"Pizza"} }
//{ "Product":{ "Name":"ToyCar"} }
模闆委托使用
回調方法執行個體:根據方法中的執行條件,我們可以按需的執行回調方法,回調方法通常是一些Action委托,可用與做一些記錄或特殊方法的再次調用。

using System;
using System.Text.Json;
namespace LanguageLearn
{
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(JsonSerializer.Serialize<Box>(box1));
Console.WriteLine(JsonSerializer.Serialize<Box>(box2));
}
}
class Logger
{
public void Log(Product product)
{
Console.WriteLine($"Product {product.Name} created at {DateTime.UtcNow}, price is {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)
{
var box = new Box();
var product = GetProduct.Invoke();
if (product.Price > 50)
{
logCallBack.Invoke(product);
}
box.Product = product;
return box;
}
}
class ProductFactory
{
public Product MakePizza()
{
Product product = new Product();
product.Name = "Pizza";
product.Price = 30;
return product;
}
public Product MakeToyCar()
{
Product product = new Product();
product.Name = "ToyCar";
product.Price = 80;
return product;
}
}
}
// out put
// Product ToyCar created at 10/10/2021 6:54:13 AM, price is 80
//{ "Product":{ "Name":"Pizza","Price":30} }
//{ "Product":{ "Name":"ToyCar","Price":80} }
回調委托使用
-
委托的高階使用
委托的單點傳播和多點傳播調用
委托的隐式異步調用以及顯式異步調用

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace LanguageLearn
{
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 action = new Action(stu1.DoHomeWork);
//action += stu2.DoHomeWork;
//action += stu3.DoHomeWork;
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();
// 顯式線程調用
//Action action1 = new Action(stu1.DoHomeWork);
//Action action2 = new Action(stu2.DoHomeWork);
//Action action3 = new Action(stu3.DoHomeWork);
//Thread thread1 = new Thread(new ThreadStart(action1));
//Thread thread2 = new Thread(new ThreadStart(action2));
//Thread thread3 = new Thread(new ThreadStart(action3));
//thread1.Start();
//thread2.Start();
//thread3.Start();
// 隐式異步調用 在.NET FrameWork中被支援
//action1.BeginInvoke(null, null);
//action2.BeginInvoke(null, null);
//action3.BeginInvoke(null, null);
for (int i = 0; i < 10; i++)
{
Console.ForegroundColor = ConsoleColor.White;
Console.WriteLine($"Main thread count {i}");
Thread.Sleep(1000);
}
}
}
class Student
{
public int Id { get; set; }
public ConsoleColor PenColor { get; set; }
public void DoHomeWork()
{
for (int i = 1; i <= 3; i++)
{
Console.ForegroundColor = PenColor;
Console.WriteLine($"Student {Id} doing homeworks {i} hour");
Thread.Sleep(1000);
}
}
}
}
委托的多點傳播和異步