摘要
任何一個C#入門的程序員都知道——當一個類型在實現接口的時候,有兩種方法實現:顯式實現、隱式實現。而且大家也都知道,當一個類型實現的兩個接口存在相同成員定義時,顯示實現可以解決這種情況。
但是,在一個命名比較規范的項目中,幾乎不可能出現上述情況。
那么,顯示實現有什么具體存在的意義嗎?
本人根據這小幾年的開發歷經,感覺顯式實現最覺的兩個作用就是:
- 改變接口成員的使用權限
- 改變接口成員的出入參數
下面本人將會對這兩種作用進一一說明
接口定義
本篇文章將會使用一個示例來講述這兩個作用。
我們先來看一下示例吧:
- 定義一個接口類型,表示一個"元件",元件擁有兩個成員
- 元件名稱,主要是用來和其它元件進行區分
- 使用元件,需要一個Object類型的參數,表示將元件使用在哪個目標對象上
- 定義一個接口類型,表示一個"元件工廠",用來生成"元件"的實例,元件工廠擁有兩個成員
- 根據指定的元件名稱,生成一個元件實例
- 注冊一個元件,只有被注冊過的元件才能夠被生產
例子還是挺簡單的,我們來把這些接口類型碼出來吧:
/// <summary> /// 一個元件接口 /// </summary> interface IComponent { /// <summary> /// 元件的名稱,用於區別於其它元件 /// </summary> String ComponentName { get; } /// <summary> /// 使用這個元件 /// <param name="target">對哪個對象使用這個元件</param> /// </summary> void Use(Object target); } /// <summary> /// 一個元件工廠接口 /// </summary> interface IComponentFactory { /// <summary> /// 創建元件 /// </summary> /// <param name="componentName"></param> /// <returns></returns> IComponent CreateComponent(String componentName); /// <summary> /// 注冊一個元件 /// </summary> /// <param name="component"></param> void RegistComponent(IComponent component); }
實現鎖與鑰匙的關系
光有接口,我們是無法工作的。因此我們開發一個叫鑰匙的元件,再開發一個密碼鎖,鑰匙可以用來開密碼鎖,當兩者的編號相同時,鎖才可以被打開。我們再做一個工廠,根據鎖的編號生成一把鑰匙,然后就可以開鎖了。
我們先用隱式實現來做這件事
/// <summary> /// 密碼鎖 /// </summary> class PasswordLocker { /// <summary> /// 開鎖用的密碼 /// </summary> public String Code { get; set; } } /// <summary> /// 鑰匙 /// </summary> class Key : IComponent { /// <summary> /// 對應的解鎖密碼,只有和鎖的Code相同時才可以開鎖 /// </summary> public string Code { get; set; } public string ComponentName { get { return this.Code; } } public void Use(object target) { //由於入參是繼承了接口的Object類型,所以必須先對入參進行類型判斷 if (target is PasswordLocker) { PasswordLocker pl = (PasswordLocker)target; if (pl.Code == this.Code) Console.WriteLine("打開鎖了"); else Console.WriteLine("未能把鎖打開"); } else Console.WriteLine("目前類型不是鎖,暫時不能使用該元件"); } } /// <summary> /// 鑰匙工廠 /// </summary> class KeyFactory : IComponentFactory { private Dictionary<String, IComponent> components = new Dictionary<string, IComponent>(); public IComponent CreateComponent(string componentName) { return components[componentName]; } /// <summary> /// 由外部創建一個元件 /// </summary> /// <param name="component"></param> public void RegistComponent(IComponent component) { components[component.ComponentName] = component; } }
然后我們再寫一代段碼使用他們:
PasswordLocker locker = new PasswordLocker(); locker.Code = "12345"; Key k = new Key(); k.Code = "12345"; KeyFactory factory = new KeyFactory(); factory.RegistComponent(k); factory.CreateComponent(locker.Code).Use(locker);
功能全部OK,但是有一些小小的瑕疵:
- 這把鑰匙從代碼上看,還可以用在不是PasswordLocker的類型上,所以Use方法中還要對入參類型進行判斷;
- 明明是鑰匙工廠,但從代碼上看生產出來的依然只是元件接口類型,注冊的時候也是,希能夠直接使用鑰匙類型;
- 如果我的元件工廠是能夠自動在實例化的時候,從數據庫里注冊好所有元件了,那我自然不希望調用工廠的人可以繼續Regist元件。
確實如此,當一個項目大到多人合作的時候,能夠有着明確的入參與返回類型和私有化不需要其它人員調用的成員,是很有必要的一件事。
所以現在回頭看剛才的那段實現代碼,我們可以提出以下要求:
- 鑰匙只能對鎖進行Use
- 工廠從數據庫中初始化所有鑰匙,然后不再開放Regist方法
- 工廠生成出來的元件就是鑰匙,而不是其它類型。
對新要求的實現:
我們通過對接口的顯示實現,以保證接口成員不可以被顯示的調用。再定義需要的public方法,調用接口成員,以達到上面三個要求:
/// <summary> /// 這是一個僅僅能用於開密碼鎖的元件,使用了現式實現改變入參 /// </summary> class PasswordLockerKey : IComponent { private string code; public PasswordLockerKey(String code) { this.code = code; } string IComponent.ComponentName { get { return this.code; } } void IComponent.Use(object target) { PasswordLocker pl = (PasswordLocker)target; if (pl.Code == this.code) Console.WriteLine("打開鎖了"); else Console.WriteLine("未能把鎖打開"); } /// <summary> /// 利用顯示繼承,隱藏了以Object作為入參的方法,並開放了一個僅僅以PasswordLocker作為入參的方法。改變了參數類型 /// </summary> /// <param name="locker"></param> public void User(PasswordLocker locker) { //將自身轉化為接口類型,再調用Use才可以使用顯式實現的方法 ((IComponent)this).Use(locker); } } /// <summary> /// 基於數據庫的鑰匙工廠,使用顯式實現改變了接口成員的訪問權限 /// </summary> class DataBaseKeyFactory : IComponentFactory { private Dictionary<String, PasswordLockerKey> keys = new Dictionary<string, PasswordLockerKey>(); /// <summary> /// 在構造函數的同時,從數據庫中加載出所有鑰匙 /// </summary> public DataBaseKeyFactory() { IComponentFactory f = (IComponentFactory)this; foreach (PasswordLockerKey k in LoadKeyFromDatabase()) { f.RegistComponent(k); } } /// <summary> /// 這是模擬的通過數據庫加載鑰匙的方法 /// </summary> /// <returns></returns> protected virtual IEnumerable<PasswordLockerKey> LoadKeyFromDatabase() { return new List<PasswordLockerKey>() { new PasswordLockerKey("12345") }; } IComponent IComponentFactory.CreateComponent(string componentName) { return keys[componentName]; } void IComponentFactory.RegistComponent(IComponent component) { keys[component.ComponentName] = (PasswordLockerKey)component; } /// <summary> /// 這里改變了原本接口的返回類型 /// </summary> /// <param name="code"></param> /// <returns></returns> public PasswordLockerKey CreateComponent(string code) { return (PasswordLockerKey)((IComponentFactory)this).CreateComponent(code); } }
代碼中模擬了一下從數據庫中取數的過程。
通過上面的方法,我們最終在使用這些類形的時候會發現,Use的入參對象只能是PasswordLocker,Factory不可以Regist成員了,Create時一定是PasswordLockerKey類型。已經基本滿足上面的需要了。
小結
在實際項目中,其實這些現象依然是很少見的。往往出現在底層接口框架被搭建好后,對接口類型進行一次基礎實現時,可能會遇到這些問題。
本人寫此文章,希望大家在遇到類似問題的時候,能夠想到可以使用接口的顯示實現來解決。
希望能夠大家帶來一些提示和幫助。
文章為作者原創,轉載請注明出處http://www.cnblogs.com/ShimizuShiori/p/5468749.html ,謝謝