C# Interface的使用方法探討

  接口是把公共實例(非靜態)的方法和屬性結合起來,以封裝特定功能的一個集合,一旦定義了接口,就能夠在類中使用實現接口中的全部成員, 接口能夠看做建立者和使用者之間的契約,一旦實現了接口,就不要輕易變更(若是須要變動接口,通常須要繼承舊接口而且添加版本號)。咱們知道在C++裏面是有純虛函數,虛繼承和多重繼承的,C#裏面爲了簡化C++的這些複雜的設施,引出了接口這個概念。
 
C#接口和類的區別:
1. 不容許使用訪問修飾符(public, private, protected,或者internal)修飾接口成員,全部的接口成員都是公共的。
2. 接口成員不能包含代碼體
3. 接口不能定義字段成員
4. 不能用關鍵字static,virtual,abstract或者sealed來定義接口成員
5. 類型定義成員是禁止的。
 
 
實現C#隱式接口:
  其實接口和C++中那種頭文件聲明一個接口而後在cpp裏面實現一遍那種作法看上去沒有什麼區別,只是C#把這個作的更純粹(自從學了C#我愈加以爲C++真是一門很囉嗦的語言)。若是一個類繼承了一個接口,那麼其對接口內的內容的實現能夠在當前類實現,也能夠在當前類的基類實現:
public class FuckBase { public void FuckSomething(int fuck) { } } public class Fuck :FuckBase, A { public int AInt { get; private set; } public void DoSomething() { } }

 

  好比上面這個例子,就在基類中實現了接口,若是要隱藏基類的接口,能夠直接new一下。
  固然了,接口是能夠繼承的,好比:
public interface A { void DoSomething(); } public interface DeriveedA: A { new void DoSomething(); }
  
  在C#的接口中能夠定義屬性,好比:
public interface DeriveedA: A { new void DoSomething(); int AInt { get; set; } }
  
  這樣定義了之後,繼承了DeriveedA的類必須實現AInt的get和set兩個屬性訪問器了,而且都必須是public,有時候咱們須要下降寫訪問器的訪問權限,咱們能夠不在接口中定義set屬性訪問器,這樣咱們能夠在類中實現有特殊訪問屬性的set屬性訪問器了,好比:
public interface DeriveedA: A { new void DoSomething(); int AInt { get; } } public class Fuck : DeriveedA { public int AInt { get; private set;//固然了這裏也能夠是protected
 } public void DoSomething() { } }
 
實現C#顯式接口:
       上面的實現都屬於C#的接口的隱式實現,那顯式實現是什麼東西?看下面的例子:
public class Starter { /// <summary>
    /// 程序入口點 /// </summary>
    /// <param name="args"></param>
    public static void Main(string[] args) { Fuck test = new Fuck(); } } public interface IFuck { void Haha(); } public class Fuck :IFuck { void IFuck.Haha() { } }
 
  這個時候若是咱們直接使用test對象,是沒法調用Haha這個方法的,由於若是一個類顯示實現了一個接口,那麼這個接口函數將是private的,外部沒法直接調用這個函數,除非把類顯式轉換爲接口:
public class Starter { /// <summary>
    /// 程序入口點 /// </summary>
    /// <param name="args"></param>
    public static void Main(string[] args) { Fuck test = new Fuck(); IFuck interfaceFuck = (IFuck)test; interfaceFuck.Haha();//這個時候至關於可使用test.Haha這個方法了
 } } public interface IFuck { void Haha(); } public class Fuck :IFuck { void IFuck.Haha()//注意顯式實現接口不能帶訪問修飾符
 { } }
  
  可能有人問爲何不把接口方法的實現定義爲private,這個在C#裏面是不容許的,若是一個類實現了一個接口(隱式實現),那麼這個接口不能是private或者是protected的,必須是public的(必須是公共接口),這其實理解起來很天然,由於每每咱們把一個類繼承一個接口,就是須要這個接口所聲明的方法。
  那麼爲何還須要顯式實現呢?從上面的例子咱們能夠看到,若是顯式實現了一個接口,那麼直接經過類來訪問接口的方法是不行的,必須是顯式轉換爲接口才能夠訪問,這就至關於了一個private的功能,好比我繼承了一個IList<T>,我可能只須要IList<T>接口的一部分,那麼我能夠把我須要的部分隱式實現,不須要的部分顯式實現,那麼這個時候我既能夠隱藏我不須要用到的方法,也能夠把它當成一個IList<T>來用(用的時候轉換爲接口就行了)。這在軟件工程裏面是很常見的,有些時候咱們寫了一個類讓框架來綁定,可是咱們不想有些接口被誤用,可是又想咱們能夠把它當作實現了這個接口的類型來用的時候,這樣的作法就是最好的。很是符合面向對象的思想。
 
  再舉幾個能夠用顯示接口的例子。
  好比如今我有一個航空公司,公司裏面有不少航班,可是其中B航班和C航班是特價航班,換句話說,就是航班之間的價格訂價是不同的,咱們先假定一下全部航班的差異就是價格。
  那麼咱們很容易想到咱們能夠實現一個這樣的航班類,可是咱們若是要查詢價格的時候,當咱們顯示查詢B,C航班價格時,使用其各自特殊的計算方法,其餘航班則選擇統一的方法,在C#裏面咱們能夠這樣實現:
public class Starter
{
    /// <summary>
    /// 程序入口點
    /// </summary>
    /// <param name="args"></param>
    public static void Main(string[] args)
    {
        Flys fly = new Flys();
        IFlyB flyB = fly;
        flyB.Cost();//計算航班B的價格

        IFlyC flyC = fly;
        flyC.Cost();//計算航班C的價格

        fly.Cost();//計算普通航班的價格

        Console.ReadKey();
    }
}
public interface IFlyB { void Cost(); } public interface IFlyC { void Cost(); } public class Flys :IFlyB,IFlyC { public void Cost() { Console.WriteLine("Other fly"); } void IFlyB.Cost() { Console.WriteLine("Fly B"); } void IFlyC.Cost() { Console.WriteLine("Fly C"); } }

  
  固然了,若是看過Effective C++的人已經知道我說的就是這本書上的例子,那麼在C++的實現方法能夠是實現一個Flys的虛基類,把Cost設爲虛函數,而後派生出FlyC和FlyB,重寫Cost,其餘航班就顯式使用虛函數的默認方法:
class Flys { public: virtual void cost()const = 0 { std::cout << "Other fly" << std::endl; } }; class FlyB :public Flys { public: void cost()const override { std::cout << "FlyB" << std::endl; } }; class FlyC :public Flys { public: void cost()const override { std::cout << "FlyC" << std::endl; } }; class OtherFly :public Flys { public: void cost()const override { Flys::cost(); } };
  
  這是一個C++的Best practice,由於這樣寫之後咱們每次定義一種Fly都必須提供Fly的定義,可使用默認定義,減小了程序員犯錯的可能, C++能夠這樣寫是由於C++的純虛函數是一個很奇葩的東西,自己它應該是相似於C#的interface纔對,可是卻有了默認行爲。
 
  第二個例子是,當我有兩個接口,可是在一個在一個接口裏面聲明瞭名爲Item的屬性,另外一個接口聲明瞭Item的一個方法,若是我一個類要同時繼承這兩個接口,怎麼辦呢?
public interface IOne { int Item { get; set; } } public interface ITwo { int Item(); } public class Hey : IOne, ITwo { public int Item { get; set;} public int Item() { throw new NotImplementedException(); } }
  
  當你寫出上面的代碼的時候,編譯器會報錯,Item具備二義性,那麼這個時候你必須顯式實現一個接口:
public interface IOne { int Item { get; set; } } public interface ITwo { int Item(); } public class Hey : IOne, ITwo { public int Item { get; set;} int ITwo.Item() { } }
  
  第三種狀況就是剛纔說的,當有些接口你不想讓別人使用,可是你卻想定義本身的版本的時候,好比你想實現一個List,而且想得到一個當Remove的時候還能夠獲取到節點的一個方法,可是IList<T>接口並無聲明這個方法,因而你能夠這樣寫:
public class ListNode<T> : IList<T> { public T RemoveAt(int index) { } void IList<T>.RemoveAt(int index) { } }
  
  這樣就實現了咱們的想法,並且接口還不容易被誤用。甚至你還能夠在顯式實現的接口void IList<T>.RemoveAt(int index)裏面拋出異常,說明不支持這種方法。
相關文章
相關標籤/搜索