1. 基本瞭解
1.1 委託簡述
官方文件
委託是一種引用型別,表示對具有特定引數列表和返回型別的方法的引用,用於將方法作為引數傳遞給其他方法,可將任何可訪問類或結構中與委託型別匹配的任何方法分配給委託
其它說明
委託在IL
中就是一個類(本質上是類),繼承與System.MulticastDelegate
類(特殊類)
委託是一個類,它定義了方法的型別,使得可以將方法當作另一個方法的引數來進行傳遞
委託型別
自定義委託:Delegate
系統內建委託:Action
,Func
委託宣告
可以宣告在類的外部也可以在內部,在IL中,無論在內外都會編譯到類的內部
委託在例項化時,需要傳入一個方法,此方法返回值,引數(型別,個數,順序)與委託一致
1.2 使用步驟
- 宣告一個委託
- 委託的例項化,傳入指定方法
- 呼叫執行
2. Delegate委託
Delegate
:常用到的一種宣告,且至少0個引數,至多32個引數,可以無返回值,也可以指定返回值型別
2.1 示例一:無參,無返回值
// 1.宣告委託
public delegate void NoReturnNoPare();
// 2.準備委託執行方法
public void Show()
{
Console.WriteLine("無參,無返回值");
}
// 3.例項,呼叫委託
public void Start()
{
NoReturnNoPare d1 = new NoReturnNoPare(this.Show);
d1.Invoke();
}
2.2 示例二:有參,無返回值
// 1.宣告委託
public delegate void NoReturnWithPare(int x, int y);
// 2.準備委託執行方法
public void Show(int x,int y)
{
Console.WriteLine("有參,無返回值");
}
// 3.例項,呼叫委託
public void Start()
{
NoReturnWithPare d2 = new NoReturnWithPare(this.Show);
d2.Invoke(1,2);
}
2.3 示例三:有參,有返回值
// 1.宣告委託
public delegate int WithReturnWithPare(int x, int y);
// 2.準備委託執行方法
public int Show(int x, int y)
{
return x + y;
}
// 3.例項,呼叫委託
public void Start()
{
WithReturnWithPare d2 = new WithReturnWithPare(this.Show);
// 返回值型別,編譯器會自動推斷
int IResult = d2.Invoke(1, 2);
Console.WriteLine(IResult);
}
3. Action委託
Action
是系統內建委託(無需宣告),是無返回值的泛型委託,至少0個引數,至多16個引數,且無返回值
3.1 示例一:無參,無返回值
// 1.定義執行方法
public void Show()
{
Console.WriteLine("無參,無返回值");
}
// 2.呼叫執行
public void Start()
{
Action action = new Action(this.Show);
// Action<int, int> action = this.Show;
action.Invoke();
}
3.2 示例二:有參,無返回值
// 1.定義執行方法
public void Show(int x, int y)
{
Console.WriteLine("有參,無返回值");
}
// 2.呼叫執行
public void Start()
{
Action<int, int> action = new Action<int,int>(this.Show);
// Action<int, int> action = this.Show;
action.Invoke(1,2);
}
3.3 示例三:使用 lambda
表示式
Action<int, int> action = (x, y) => { };
action.Invoke(1, 2);
3.4 示例四:將委託作為方法引數
public void Start()
{
Action<int> action = (x) => { Console.WriteLine(x); };
Show(action, 2);
}
public void Show<T>(Action<T> ac, T inputParam)
{
ac(inputParam);
}
4. Func委託
Func
是有返回值的泛型委託,至少0個引數,至多16個引數,根據返回值泛型返回;必須有返回值,不可void
,且最後一位泛型型別,為返回值型別
4.1 示例一:無參,有返回值
public void Start()
{
Func<string> func = new Func<string>(this.Show);
string IResult = func.Invoke();
Console.WriteLine(IResult);
}
public string Show()
{
return "libai";
}
4.2 示例二:有參,有返回值
public void Start()
{
Func<int, string> func = new Func<int, string>(this.Show);
string IResult = func.Invoke(1);
Console.WriteLine(IResult);
}
public string Show(int i)
{
return "libai\t" + i;
}
4.3 示例三:使用lambda
表示式
public void Start()
{
Func<int> func1 = () => { return 1; };
int IResultInt = func1.Invoke();
Console.WriteLine(IResultInt);
Func<int, string> func2 = (i) => { return i.ToString(); };
string IResult = func2.Invoke(1);
Console.WriteLine(IResult);
}
4.4 示例四:將委託作為方法引數
例子一:簡化
public void Start()
{
Func<int, int, int> func = (x, y) => { return x + y; };
int IResultInt = Test(func, 1, 2);
Console.WriteLine(IResultInt);
}
public int Test<T1, T2>(Func<T1, T2, int> func, T1 a, T2 b)
{
return func(a, b);
}
示例二:一般寫法
static void Main(string[] args)
{
Console.WriteLine(Test<int,int>(Fun,100,200));
Console.ReadKey();
}
public static int Test<T1, T2>(Func<T1, T2, int> func, T1 a, T2 b)
{
return func(a, b);
}
private static int Fun(int a, int b)
{
return a + b;
}
5. 鏈式委託
5.1 文件說明
官方文件
委託物件的一個有用屬性在於可通過使用 +
運算子將多個物件分配到一個委託例項,多播委託包含已分配委託列表,此多播委託被呼叫時會依次呼叫列表中的委託;僅可合併型別相同的委託
-
運算子可用於從多播委託中刪除元件委託,順序,從下至上(委託列表中沒有移除的委託時不會報錯)+
運算子可用於將委託元件新增到委託列表,順序,從上而下
在執行有返回值的委託鏈時,只能得到最後一個委託的結果
其它文件
委託鏈(多播委託)是一個由委託組成的連結串列,而不是一個新的東西,所有的自定義委託都直接整合自System.MulticastDelegate
型別,這個型別即是為委託鏈而設計的
鏈式委託是指一個委託的連結串列,而不是指另外一類特殊的委託,當執行鏈上的一個方法時,後續委託將會被依此執行
System.MuticastDelegate
定義了對鏈式委託的支援,在System.Delegate
的基礎上,增加了一個指向後續委託的指標,這樣就實現了一個簡單的連結串列結構
5.2 示例一:統一執行
public void Start()
{
NoReturnWithPare noReturnWith = new NoReturnWithPare(this.Fun1);
noReturnWith += this.Fun2;
noReturnWith += this.Fun1;
noReturnWith.Invoke(1, 2);
}
public void Fun1(int x, int y)
{
Console.WriteLine("Fun1:\t" + x + y);
}
public void Fun2(int x, int y)
{
Console.WriteLine("Fun2:\t" + x + y);
}
5.3 示例二:逐個執行
注意:逐個執行時,單項不能用var
宣告,必須使用委託的具體型別
逐個指定,返回值,問題,b委託使用a的返回值?
public void Start()
{
NoReturnWithPare noReturnWith = new NoReturnWithPare(this.Fun1);
noReturnWith += this.Fun2;
noReturnWith += this.Fun1;
foreach (NoReturnWithPare item in noReturnWith.GetInvocationList())
{
item.Invoke(1,2);
}
}
public void Fun1(int x, int y)
{
Console.WriteLine("Fun1:\t" + x + y);
}
public void Fun2(int x, int y)
{
Console.WriteLine("Fun2:\t" + x + y);
}
5.4 示例三:責任鏈模式
典型案例:用程式碼模擬,貓叫了,狗叫了,然後老鼠跑了
普通實現
using System;
namespace de2
{
class Program
{
static void Main(string[] args)
{
Cat cat = new Cat();
cat.Miao();
}
}
public class Cat
{
public void Miao()
{
Console.WriteLine("貓叫了");
new Dog().Wang();
new Mouse().Run();
}
}
public class Dog
{
public void Wang()
{
Console.WriteLine("狗叫了");
}
}
public class Mouse
{
public void Run()
{
Console.WriteLine("老鼠跑了");
}
}
}
使用責任鏈模式實現(委託)
using System;
namespace de2
{
class Program
{
static void Main(string[] args)
{
Cat cat = new Cat();
cat.miaoAction += new Dog().Wang;
cat.miaoAction += new Mouse().Run;
cat.MiaoDelegate();
}
}
public class Cat
{
public void Miao()
{
Console.WriteLine("貓叫了");
}
public Action miaoAction;
public void MiaoDelegate()
{
this.Miao();
this.miaoAction.Invoke();
}
}
public class Dog
{
public void Wang()
{
Console.WriteLine("狗叫了");
}
}
public class Mouse
{
public void Run()
{
Console.WriteLine("老鼠跑了");
}
}
}
使用責任鏈模式實現(抽象方法)
using System;
using System.Collections.Generic;
namespace de2
{
class Program
{
static void Main(string[] args)
{
Cat cat = new Cat();
cat.Add(new Dog());
cat.Add(new Mouse());
cat.AbsServer();
}
}
public interface IAbsServer
{
void Do();
}
public class Cat : IAbsServer
{
private List<IAbsServer> list = new List<IAbsServer>();
public void Add(IAbsServer absServer)
{
list.Add(absServer);
}
public void AbsServer()
{
this.Do();
foreach (var item in list)
{
item.Do();
}
}
public void Miao()
{
Console.WriteLine("貓叫了");
}
public void Do()
{
this.Miao();
}
}
public class Dog : IAbsServer
{
public void Do()
{
this.Wang();
}
public void Wang()
{
Console.WriteLine("狗叫了");
}
}
public class Mouse : IAbsServer
{
public void Do()
{
this.Run();
}
public void Run()
{
Console.WriteLine("老鼠跑了");
}
}
}
使用責任鏈模式實現(事件)
using System;
namespace de2
{
class Program
{
static void Main(string[] args)
{
Cat cat = new Cat();
cat.miaoEvent += new Dog().Wang;
cat.miaoEvent += new Mouse().Run;
cat.MiaoEvent();
}
}
public class Cat
{
public void Miao()
{
Console.WriteLine("貓叫了");
}
/// <summary>
/// 事件,只能在事件所在類(本身類,子類不可)的內部 Invoke 執行
/// </summary>
public event Action miaoEvent;
public void MiaoEvent()
{
this.Miao();
this.miaoEvent.Invoke();
}
}
public class Dog
{
public void Wang()
{
Console.WriteLine("狗叫了");
}
}
public class Mouse
{
public void Run()
{
Console.WriteLine("老鼠跑了");
}
}
}
5.5 補充說明
鏈式委託的執行順序是:按照委託鏈上的順醋從當前委託開始依次往後執行,如果有需要可以使用GetInvocationList()
方法來獲得委託鏈上所有需要執行的委託,並且按照任何希望的順序去逐個執行(Invoke
)
委託可以是帶有返回值的方法,但多餘一個帶返回值的方法被新增到委託鏈中時,程式設計師需要手動地呼叫委託鏈上的每個方法,否則委託使用者智慧得到委託鏈上最後一個被執行的方法的返回值
委託的應用場合通常是任務的執行者把細節工作進行再分配,執行者確切地知道什麼工作將要被執行,但卻把執行細節委託給其他元件、方法或者程式集
6. 委託事件
事件(Event):是委託的例項,在定義委託是加了enevt
關鍵字
enevt
關鍵字,限定許可權,只能在事件所在類中呼叫事件
6.1 示例一:自定義標準事件
模擬:使用者訂閱手機降價事件,當降價時用於購買手機
using System;
namespace de3
{
class Program
{
static void Main(string[] args)
{
Phone phone = new Phone
{
name = "vivo",
Price = 1999
};
phone.DiscountEventHandler += new User() { name = "李白" }.Buy;
phone.Price -= 400;
}
}
// 事件額外資訊
public class EventPara
{
public int oValue { get; set; }
public int nValue { get; set; }
}
public delegate void CostomEventHandler(object sender, EventPara para);
// 手機,釋出者,釋出事件並且在滿足條件情況下執行事件
public class Phone
{
public string name { get; set; }
private int price;
public int Price
{
set
{
if (value < this.price)
{
this.DiscountEventHandler?.Invoke(this, new EventPara
{
oValue = this.price,
nValue = value
});
}
this.price = value;
}
get { return this.price; }
}
public event CostomEventHandler DiscountEventHandler;
}
// 訂戶,關注事件,事件發生後執行動作
public class User
{
public string name { get; set; }
// 買手機
public void Buy(object sender, EventPara para)
{
Phone phone = (Phone)sender;
Console.WriteLine($"手機:{phone.name}\t打折前:{para.oValue}\t打折後:{para.nValue}");
Console.WriteLine("購買手機!");
}
}
}
標註:委託事件實際應用還不太熟,示例做參考即可
7. 擴充套件補充
7.1 委託的內部結構
IL
語言的無參無返回值的委託結構(編譯後)
.class nested public auto ansi sealed NoReturnNoPare
extends [mscorlib]System.MulticastDelegate
{
// Methods
.method public hidebysig specialname rtspecialname
instance void .ctor (
object 'object',
native int 'method'
) runtime managed
{
} // end of method NoReturnNoPare::.ctor
.method public hidebysig newslot virtual
instance void Invoke () runtime managed
{
} // end of method NoReturnNoPare::Invoke
.method public hidebysig newslot virtual
instance class [mscorlib]System.IAsyncResult BeginInvoke (
class [mscorlib]System.AsyncCallback callback,
object 'object'
) runtime managed
{
} // end of method NoReturnNoPare::BeginInvoke
.method public hidebysig newslot virtual
instance void EndInvoke (
class [mscorlib]System.IAsyncResult result
) runtime managed
{
} // end of method NoReturnNoPare::EndInvoke
} // end of class NoReturnNoPare
7.2 呼叫委託
使用委託例項呼叫,引數寫在括號中
NoReturnNoPare d1 = new NoReturnNoPare(this.Show);
d1();
使用例項的
Invoke()
方法呼叫,引數寫在方法中
NoReturnNoPare d1 = new NoReturnNoPare(this.Show);
d1.Invoke();
7.3 Predicate<T>
委託
說明:不常用,僅作為了解(看個人情況)
Predicate
是返回bool
型的泛型委託,至少1個引數,至多1個引數,返回值固定為bool
官方示例
using System;
using System.Drawing;
public class Example
{
public static void Main()
{
Point[] points = { new Point(100, 200),
new Point(150, 250), new Point(250, 375),
new Point(275, 395), new Point(295, 450) };
Predicate<Point> predicate = FindPoints;
Point first = Array.Find(points, predicate);
Console.WriteLine("Found: X = {0}, Y = {1}", first.X, first.Y);
}
private static bool FindPoints(Point obj)
{
return obj.X * obj.Y > 100000;
}
}