C#委托基礎:談委托和接口
本文是博客園中近日關于C#委托的討論所衍生出來的一個系列中的***篇,文章作者麒麟.NET對C#委托的內涵和外延進行了討論。用麒麟.NET的話來說:“委托是.NET Framework中一個非常炫的特性,絕不會向有些評論里說的那樣,根本沒有機會接觸。恰恰相反,我們幾乎每天都會接觸委托,使用委托?!?/P>
那么,先從示例開始,了解什么是C#委托吧。
從示例開始
假設一個系統(tǒng)的用戶登錄模塊有如下所示的代碼
- class User
- {
- public string Name { get; set; }
- public string Password { get; set; }
- }
- class UserService
- {
- public void Register(User user)
- {
- if (user.Name == "Kirin")
- {
- Log("注冊失敗,已經包含名為" + user.Name + "的用戶");
- }
- else
- {
- Log("注冊成功!");
- }
- }
- privte void Log(string message)
- {
- Console.WriteLine(message);
- }
- }
UserService類封裝用戶登錄的邏輯,并根據(jù)不同的登錄情況向控制臺打印不同的日志內容。當程序關閉時,所記錄的日志自然也隨之消失。
客戶端的代碼為
- class Program
- {
- static void Main(string[] args)
- {
- User user = new User { Name = "Kirin", Password = "123" };
- UserService service = new UserService();
- service.Register(user);
- Console.ReadLine();
- }
- }
使用策略模式
然而這樣的設計肯定是無法滿足用戶的需求的,用戶肯定希望能夠查看以前的日志記錄,而不僅僅是程序打開以后的內容。如果我們僅僅修改Log方法的實現(xiàn),那么用戶需求再次改變時我們該如何處理呢?難道要無休止地修改Log方法嗎?
既然日志記錄的方式是變化的根源,我們自然會想到將其進行封裝。我們創(chuàng)建一個名為ILog的接口。
- interface ILog
- {
- void Log(string message);
- }
并創(chuàng)建兩個實現(xiàn)了ILog的類,ConsoleLog和TextLog,分別用來向控制臺和文本文件輸出日志內容。
- class ConsoleLog : ILog
- {
- public void Log(string message)
- {
- Console.WriteLine(message);
- }
- }
- class TextLog : ILog
- {
- public void Log(string message)
- {
- using (StreamWriter sw = File.AppendText("log.txt"))
- {
- sw.WriteLine(message);
- sw.Flush();
- sw.Close();
- }
- }
- }
在UserService類中添加一個ILog類型的屬性LogStrategy。
- class UserService
- {
- public ILog LogStrategy { get; set; }
- public UserService()
- {
- LogStrategy = new ConsoleLog();
- }
- public void Register(User user)
- {
- if (user.Name == "Kirin")
- {
- LogStrategy.Log("注冊失敗,已經包含名為" + user.Name + "的用戶");
- }
- else
- {
- LogStrategy.Log("注冊成功!");
- }
- }
- }
客戶端代碼變?yōu)槿缦滦问健?/P>
- class Program
- {
- static void Main(string[] args)
- {
- User user = new User { Name = "Kirin", Password = "123" };
- UserService service = new UserService { LogStrategy = new TextLog() };
- service.Register(user);
- Console.ReadLine();
- }
- }
在聲明UserService的時候,還可以將將LogStrategy設置為TextLog。這樣在UserService進行邏輯處理時,使用的LogStrategy即為TextLog,日志將輸出到文本文件中。
我們在干什么?我們在重構。重構的結果是什么?重構的結果是實現(xiàn)了一個簡單的策略模式。
使用委托
然而策略模式仍然不能滿足客戶的需求,這是為什么呢?
1. 用戶也許會希望自定義Log的實現(xiàn)。當然,你可以通過在客戶代碼處擴展ILog來實現(xiàn)自己的日志記錄方式。如
- class TextBoxLog : ILog
- {
- private TextBox textBox;
- public TextBoxLog(TextBox textBox)
- {
- this.textBox = textBox;
- this.textBox.Multiline = true;
- }
- public void Log(string message)
- {
- textBox.AppendText(message);
- textBox.AppendText(Environment.NewLine);
- }
- }
但這種方案是否過于復雜呢?如果用戶希望在ListView或其他控件上顯示,是否需要逐個創(chuàng)建新類呢?并且這樣的實現(xiàn)是否與客戶端的耦合過于緊密呢?比如用戶希望在ListView的各個列中顯示日志內容、時間、來源等不同內容,那么在ListViewLog中對ListView硬編碼是否很難重用呢?
2. 用戶也許會希望同時使用多種日志記錄方式。比如,同時向控制臺、文本文件、客戶端控件和事件查看器中輸出日志。你當然可以在UserService中維護一個List<ILog>,但這時UserService的職責過多,顯然違反了SRP。
下面介紹本文的主角:委托。
我們首先來創(chuàng)建一個名為Log的委托,它接收一個string類型的參數(shù)。
- public delegate void Log(string message);
然后在UserService類中添加一個Log委托類型的屬性LogDelegate。
- class UserService
- {
- public Log LogDelegate { get; set; }
- // …
- }
在客戶端,我們直接聲明兩個靜態(tài)方法,它們都包含一個string類型的參數(shù),并且沒有返回值。
- static void LogToConsole(string message)
- {
- Console.WriteLine(message);
- }
- static void LogToTextFile(string message)
- {
- using (StreamWriter sw = File.AppendText("log.txt"))
- {
- sw.WriteLine(message);
- sw.Flush();
- sw.Close();
- }
- }
客戶端聲明UserService的代碼變?yōu)?/P>
- static void Main(string[] args)
- {
- User user = new User { Name = "Kirin", Password = "123" };
- UserService service = new UserService();
- service.LogDelegate = LogToConsole;
- service.LogDelegate += LogToTextFile;
- service.Register(user);
- Console.ReadLine();
- }
在構造委托時,我們還可以使用匿名方法和Lambda表達式,在老趙的文章中詳細闡述了這些寫法的演變。
對于何時使用委托,何時使用接口(即策略模式),MSDN中有明確的描述:
在以下情況下,請使用委托:
◆當使用事件設計模式時。
◆當封裝靜態(tài)方法可取時。
◆當調用方不需要訪問實現(xiàn)該方法的對象中的其他屬性、方法或接口時。
◆需要方便的組合。
◆當類可能需要該方法的多個實現(xiàn)時。
在以下情況下,請使用接口:
◆當存在一組可能被調用的相關方法時。
◆當類只需要方法的單個實現(xiàn)時。
◆當使用接口的類想要將該接口強制轉換為其他接口或類類型時。
◆當正在實現(xiàn)的方法鏈接到類的類型或標識時:例如比較方法。
您可能覺得上面的例子闡述委托和接口有些過于牽強,事實上有些時候的確很難選擇使用接口還是委托。Java中沒有委托,但所有委托適用的情況同樣可以使用包含單一方法的接口來實現(xiàn)的。在某種程度上,可以說委托是接口(僅定義了單一方法)的一種輕量級實現(xiàn),它更靈活,也更方便。
以上就是對于C#委托中一些基本概念的介紹。
【編輯推薦】