標籤:
摘要
任何一個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 ,謝謝
C#介面顯示實現在實際開發中的作用