Delegate(委托)
在前面lambda章節中稍微提了一下委托,今天這章就讓我們來深究一下委托。
委托的本質是一種類,他是繼承MulticastDelegate類的。
而聲明委托的關鍵字的delegate,如:public delegate void NoReturnNoParaOutClass();
但是剛才也講了委托一種類,那按照類的聲明,我們應該可以這樣聲明一個委托。
public class NoReturnNoParaOutClass: System.MulticastDelegate { }
只不過由於這種類比較特殊,因為它由於框架限定,這種做法在.net框架中是不被允許的,所以我們只能通過delegate關鍵字來聲明委托。
下面講講委托應該怎麽用。
用委托分為三個步驟:
1)聲明委托
2)委托實例化
3)委托的調用
public delegate void NoReturnNoPara();//1聲明委托 NoReturnNoPara method = new NoReturnNoPara(this.DoNothing);//2 委托的實例化
method.Invoke();//3 委托的調用 private void DoNothing() { Console.WriteLine("This is DoNothing"); }
在前面的章節也講過,實例化委托時必須保證委托的簽名和方法的簽名保持一致。
再說個例子來闡述委托的用法。
假設有一個這樣的需求,有一個汽車工廠,生產一款汽車。按照一般的做法應該是這樣的
public void BuildCar() { Console.WriteLine("造一個發動機"); Console.WriteLine("造一個外殼"); Console.WriteLine("造一個底盤"); Console.WriteLine("造四個輪子"); Console.WriteLine("組裝成一輛車"); Console.WriteLine("造好了一輛車。。"); }
但是由於社會在進步,車子的性能也在提升,車子的發動機也需要跟新換代,在以前只有自然吸氣,現在需要增加 渦輪增壓和電動,按照一般的改法的話。
public void BuildCar(EngineType engineType) { if (engineType == EngineType.NaturalInspiration) { Console.WriteLine("造一個自然吸氣發動機"); } else if (engineType == EngineType.Turbo) { Console.WriteLine("造一個渦輪增壓發動機"); } else if (engineType == EngineType.Electric) { Console.WriteLine("造一個電池發動機"); } Console.WriteLine("造一個外殼"); Console.WriteLine("造一個底盤"); Console.WriteLine("造四個輪子"); Console.WriteLine("組裝成一輛車"); Console.WriteLine("造好了一輛GTR。。"); } public enum EngineType { NaturalInspiration = 0, Turbo = 1, Electric = 2 }
這種做法通過上端程序調用時傳遞一個參數來判斷生產哪一種發動機。但是這樣做對工廠的要求太大,萬一以後出現了太陽能的豈不是對工廠的整體生產線都要進行修改,這樣做的成本太大,不切合實際,而且現在的工廠一般都是只做組裝工作,生產的任務都是外包出去。那麽我們程序應該怎麽改呢,畢竟程序源自於生活。要符合實際情況才行。
這個時候就需要使用委托了
我們把生產發動機都給剝離出來,在生產廠家只需要組裝就行。
public void BuildEngineNaturalInspiration() { Console.WriteLine("造一個自然吸氣發動機"); } public void BuildEngineTurbo() { Console.WriteLine("造一個渦輪增壓發動機"); } public void BuildEngineElectric() { Console.WriteLine("造一個電池發動機"); }
那後再類中聲明一個委托,並修改BuildCar方法。
public void BuildCar(BuildEngineDelegate method) { method.Invoke(); Console.WriteLine("造一個外殼"); Console.WriteLine("造一個底盤"); Console.WriteLine("造四個輪子"); Console.WriteLine("組裝成一輛車"); Console.WriteLine("造好了一輛GTR。。"); } public delegate void BuildEngineDelegate();
在上端調用時直接傳遞一個方法,內部調用一下完成組裝,這樣做生產廠家就不必考慮應該怎麽做了,你調用的時候直接給我,而我這裏就完成一個組裝工作。
CarFactory.BuildEngineDelegate method = new CarFactory.BuildEngineDelegate carFactory.BuildEngineNaturalInspiration); carFactory.BuildCar(method);
這樣做廠家就對發動機怎麽做的依賴性不那麽強了。
再來講個黑科技,有許多人寫程序時不願意寫異常處理。在這裏可以直接寫一個異步處理,通過委托可以綁定在任何方法上。
/// <summary> /// 通用的異常處理 /// </summary> /// <param name="act">對應任何的邏輯</param> public static void SafeInvoke(Action act) { try { act.Invoke(); } catch (Exception ex)//按異常類型區分處理 { Console.WriteLine(ex.Message); } }
調用時只需要這樣做就可以一步完成異步處理功能。
CarFactory.SafeInvoke(() => carFactory.BuildCar(method));
最後在講講多播委托,提到多播委托就不得不提提設計模式中的觀察者模式。
我們假設有一只貓叫了一聲,然後狗叫,老鼠跑,小孩就哭了,媽媽也醒了。爸爸就大叫,鄰居也被吵醒,小偷也趕快溜了。
就因為貓的叫,導致一系列的觸發動作
public class Cat { public void Miao() { Console.WriteLine("{0} Miao", this.GetType().Name); new Dog().Wang(); new Mouse().Run(); new Baby().Cry(); new Mother().Wispher(); new Brother().Turn(); new Father().Roar(); new Neighbor().Awake(); new Stealer().Hide(); } }
為了篇幅,其他類就不寫上來了,都是簡單的一些方法。。
假如貓叫一聲,不是狗先叫,而是老鼠先跑,那豈不又要修改貓這個類,再說老鼠跑不跑管貓什麽事,我叫我叫的,你愛幹啥幹啥,管我什麽事,
但是這樣的程序設計就必須修改貓這個類的方法。這個時候我們就要思考了,可不可把方法抽離出來,別那麽依賴貓。其實用委托就可以實現這個功能。
聲明一個委托,並在貓類中調用委托。
public Action MiaoAction;
public event Action MiaoActionEvent;
public void MiaoEvent()
{
Console.WriteLine("{0} MiaoActionEvent", this.GetType().Name);
if (MiaoActionEvent != null)
MiaoActionEvent.Invoke();
}
這個上端調用只需要這樣做。
Console.WriteLine("************cat.MiaoEvent();***********"); cat.MiaoActionEvent += new Dog().Wang;//訂閱 cat.MiaoActionEvent += new Mouse().Run; cat.MiaoActionEvent += new Baby().Cry; cat.MiaoActionEvent += new Mother().Wispher; cat.MiaoActionEvent += new Brother().Turn; cat.MiaoActionEvent += new Father().Roar; cat.MiaoActionEvent += new Neighbor().Awake; cat.MiaoActionEvent += new Stealer().Hide; cat.MiaoEvent();
在上面中提到了Event關鍵字,它就是事件,那麽委托和事件是怎樣的關系呢。
委托是一種類型,而事件是委托的一個實例
其實事件控制了實例的使用權限,更加安全。
事件不能再其他類中調用,只能在聲明事件的類中調用,這就保證了事件的安全。
在觀察者模式中。分為發布者,訂戶,和訂閱者。
在本例中,貓就是那個發布者,而狗、小孩就是訂戶,最後調用時,上端是訂閱者。而事件的發布只能聲明在引起這一系列動作類中。
那麽使用委托的意義是什麽呢,使用委托主要是為了解耦,是程序之間的依賴關系不是那麽強。還有多線程,這個以後再講。
Delegate(委托)