C#委托
23考完復變打算更博的,但是那天頂著38.5°C考完復變之后實在頂不住了,歇了幾天
委托
委托(delegate)是C/C++里面函式指標的升級版,如果你有一定的C基礎,那就知道委托的作用了,在計算機里面一切皆為地址,變數(資料)是以某個地址為起點的一段記憶體中所存盤的值,函式(演算法)是以某個地址為起點的一段記憶體中所存盤的一組機器語言指令,函式的呼叫有兩種方法:直接呼叫和間接呼叫,直接呼叫就是直接通過函式的名字來呼叫,CPU通過函式的名字找到函式存盤的位置進而執行;間接呼叫是用函式指標來呼叫,CPU通過函式指標存盤的地址找到函式所在的地方進而執行,
C#中的委托是經過包裝的函式指標,讓你感受不到它是一個指標,但是實際用起來,就是函式指標的用法,C#中的委托可以自己寫一個,或者使用C#為你準備好的一些委托,比如Action(無回傳值的函式)、Func(有回傳值的函式),
說了這么多,委托有什么用呢?拿兩個經典例子來說:模板方法(template method)和回呼方法(callback),
多說無益,看例子:
模板方法
using System;
namespace SomeNote
{
class Program
{
static void Main(string[] args)
{
ProductFactory productFactory = new ProductFactory();
WrapFactory wrapFactory = new WrapFactory();
Box box1 = new Box();
Box box2 = new Box();
Func<Product> fun1 = productFactory.MakePizza; //Func這個委托是C#準備好的,它接受一個帶回傳值,但是沒有引數的函式
Func<Product> fun2 = productFactory.MakeToyCar; //Func是一個泛型委托,尖括號里面是它的回傳值型別
box1 = wrapFactory.WrapProduct(fun1);
box2 = wrapFactory.WrapProduct(fun2);
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;
}
}
}
這就是模板方法,借用指定的外部方法來產生結果,WrapProduct這個方法是非常好的一個處理方式,你需要什么就給它什么,模板方法一般出現在代碼中間,模板方法的委托一般有回傳值,放到方法體里面做各種處理,
程式的執行結果如下:

回呼方法
using System;
namespace SomeNote
{
class Program
{
static void Main(string[] args)
{
WrapFactory wrapFactory = new WrapFactory();
ProductFactory productFactory = new ProductFactory();
Box box1 = new Box();
Box box2 = new Box();
Logger logger = new Logger();
Action<Product> action = new Action<Product>(logger.Log); //Action也是C#為我們準備好的一個泛型委托
Func<Product> func1 = new Func<Product>(productFactory.MakePizza); //它接收一個沒有回傳值,引數只有一個的函式,尖括號里面是函式的引數型別
Func<Product> func2 = new Func<Product>(productFactory.MakeToyCar); //不知道你有沒有發現這里宣告委托和上面的例子不一樣
box1 = wrapFactory.WrapProduct(func1, action);
box2 = wrapFactory.WrapProduct(func2, action);
Console.WriteLine(box1.Product.Name);
Console.WriteLine(box2.Product.Name);
}
}
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) //價格大于50就輸出它的價格
{
logCallback(product);
}
box.Product = product;
return box;
}
}
class ProductFactory
{
public Product MakePizza()
{
Product product = new Product();
product.Name = "Pizza";
product.Price = 60;
return product;
}
public Product MakeToyCar()
{
Product product = new Product();
product.Name = "Toy Car";
product.Price = 30;
return product;
}
}
class Logger //這個類只有一個方法,用來實作回呼
{
public void Log(Product product)
{
Console.WriteLine(product.Price);
}
}
}
回呼方法的例子我只是在模板方法的基礎上稍作修改,注意到WrapProduct這個方法多了一個引數,多接收了一個委托,在方法體里面,多了一個判斷價錢是否大于50,這里的Action為Log,輸出Product的價格,回呼方法相當于一條流水線,你符合就呼叫,不符合就不呼叫,它常常位于代碼末,它使用的委托沒有回傳值,
最后關注一下15、16、17行的代碼,與模板方法的例子不一樣,這里委托的宣告一大串,很復雜,這是什么意思呢?在上一個例子,我們沒有說明Func系結的函式是什么型別的,這是因為編譯器會幫我們做推斷,而這個例子我們把它說清楚了,它就是一個Func<Product>型的委托,系結的函式是MakePizza,其實編譯器很聰明的,一般我們不用完整說出來它的型別,編譯器會幫我們推斷,
最終,程式的運行結果:

Caution!
委托有一個很突出的特點:難精通、易使用、功能強大,委托濫用會產生很多不好的后果:
- 這是一種方法級別的緊耦合,現實作業中要謹慎使用
- 使可讀性下降,debug難度大大增加
- 把委托回呼、異步呼叫和多執行緒糾纏在一起,會讓代碼難以閱讀和維護
- 委托使用不當可能造成記憶體泄露和程式性能下降
In a word,委托是好東西,但是用之前要三思,
碎碎念
因為復習復變,一個星期沒寫碼了,昨天頂著38度做了博客園客制化,侯捷的課還剩下3節,我覺得我挺拼的,但是好像沒什么結果,下周作業室申請那邊要答辯了,希望一切順順利利吧,為了R社,也為了自己,(其實今天這篇博客也是受狒狒啟發才寫的)
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/227354.html
標籤:.NET技术
上一篇:.NET 5 ORM 八大實用技巧 干貨 - SqlSugar ORM
下一篇:liandongtiaoshi
