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)里面拋出異常,說明不支持這種方法。
 
 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM