###### tags: `C#` # 介面(Interface) ## 說明 介面可以視為只包含抽象成員的一種特殊類別,它只定義成員的介面規格,成員內容則由實作介面的衍生類別根據自身需求提供專屬的實作。**介面最大的好處在於將方法成員的規格與實作分開,解決了類別繼承所造成的問題。** 繼承機制儘管威力強大,卻相對的衍生出其他的問題,整個繼承關係裏,衍生類別同時被允許存取基礎類別`public`以及`protected`層級的相關成員,當繼承架構日益複雜,基礎類別的修改,往往對大量的衍生類別形成不同程度的影響,造成大型系統日後演進的因難,同時降低系統擴充的彈性。 介面將成員規格與實作分開,將類別公用介面從實作中抽離出來,成為獨立不含方法實作的純抽象類別,因此當你定義一個繼承介面的衍生類別,只需遵守方法的宣告語法,再自行定義專屬的方法實作,如此一來即可避免基礎類別與衍生類別之間因為繼承所帶來的問題,另外一方面,由於繼承介面類別可以完全自由實作介面定義的方法內容,在某種程度上亦達到了多型的目的。 * **介面為只有宣告成員,而沒有實作的類別** * **介面只能定義屬性、方法、事件、索引,且不包含實作(Implementation)這些成員的程式碼** * **介面可以有 0 個或多個成員** * **介面的成員只能是屬性、方法、事件、索引** ```csharp= interface 介面 { int 屬性 { get; } //屬性 public void 方法();//方法 event MyEvent 事件;//事件 int this[int index] {get; set;}//索引 } ``` * **一個類別只能繼承一個父類別,但可以實作(繼承)多個介面** ```csharp= class 類別 : 介面1, 介面2, 介面3 { xxxxx } ``` * **介面可以繼承多個介面** * **介面不可以被實體化 ⇒ 即不可以 new 介面** * **若類別繼承介面,則要實作介面的所有成員** ## 宣告介面 由於介面本身需由外部類別實作其所有方法,因此所有介面方法一律均為 **public** 層級,**不需使用存取修飾詞**,當你使用存取修飾詞於方法宣告時,將會產生修飾詞無效的編譯錯誤。 ```csharp= interface 介面名稱 { // interface 規格定義 … } ``` ## 實作介面 **實作介面的類別必須實作其所有方法成員,也就是必須表現介面所有的行為,即使沒有實作其內容,還是必須在實作類別裏,定義介面的方法。**  上圖左邊是一個包含兩個方法成員定義的介面 A,由於這是一個介面,因此其中的方法成員只定義而沒有任何實作內容,右邊則是實作此介面的兩個類別: B 與 C 。 類別 B 在功能面上,只需提供介面 A 方法成員`aMethod`的實作,但是由於其實作了介面,因此必須同時定義`bMethod`,即便只是定義此方法無任何實作內容,類別 C 亦同時實作了介面 A 與其所有成員。 ## 實作要點 1. **分開方法的定義與實作**:介面抽離方法的定義與實作,你可以將其視為一種完全只有抽象方法的純抽象類別。 2. **避免繼承架構的相依性**:介面最大的好處在於避免繼承架構關系裏,基礎類別與衍生類別之間緊密的相依性,提供最大的設計彈性。 3. **公開所有成員**:所有的介面方法成員均只包含方法名稱,且一律為 public 存取層級。 4. **實作所有介面成員**:繼承介面的衍生類別必須實作所有的介面成員,而且成員的名稱、參數、傳回值型別都必須完全符合,你可以實作一個不包含任何內容的方法,避免編譯錯誤,但是無論如何,衍生類別一定要完成所有的介面方法實作。 ## 範例 1. 建立介面型別`IMeasure`,`Length`、`Area`以及`Volume`是三個介面方法規格,後續實作此介面的類別必須依據這裏的定義,完成三個方法的內容實作。 ```csharp= interface IMeasure { void Length(double len); void Area(double len); void Volume(double len); } ``` 2. 類別`Square`實作`IMeasure`介面,此類別實作三個介面定義的方法成員`Length`、`Area`以及`Volume` ```csharp= class Square : IMeasure { public void Length(double len) { double squareLength = 4 * len; Console.WriteLine($"邊長為 {len} 的正方形周長 = {squareLength}"); } public void Area(double len) { double area = Math.Pow(len, 2); Console.WriteLine($"邊長為 {len} 的正方形面積 = {area}"); } public void Volume(double len) { double volume = Math.Pow(len, 3); Console.WriteLine($"邊長為 {len} 的立方體體積 = {volume}"); } } ``` 3. 建立`Square`物件實體`square`,然後引用實作介面的方法成員 ```csharp= class Program { static void Main(string[] args) { Square square = new Square(); IMeasure myIMeasure = square ; myIMeasure.Length(5); myIMeasure.Area(5); myIMeasure.Volume(5); Console.ReadLine(); } } ``` 4. 執行結果 ```htmlembedded= 邊長為 5 的正方形周長 = 20 邊長為 5 的正方形面積 = 25 邊長為 5 的立方體體積 = 125 ``` ## 多型(Polymorphism) 1. 建立介面型別`IMeasure`,定義`Length`方法 ```csharp= interface IMeasure { void Length(double len); } ``` 2. 類別`Square`實作`IMeasure`介面,實作介面定義的方法成員`Length` ```csharp= class Square : IMeasure { public void Length(double len) { double squareLength = 4 * len; Console.WriteLine($"邊長為 {len} 的正方形周長 = {squareLength}"); } } ``` 3. 類別`Circle`實作`IMeasure`介面,實作介面定義的方法成員`Length` ```csharp= class Circle : IMeasure { public void Length(double r) { double circleLength = 2 * Math.PI * r; Console.WriteLine($"邊長為 {r} 的圓周長= {circleLength}"); } } ``` 4. 方法`LengthMeasure`接受任何`IMeasure`介面的實作類別物件以及要計算的長度值,於方法中透過`measure`引用`Length`方法完成邊長計算,由於所有`IMeasure`介面的實作類別一定實作`Length`方法,因此傳入任何實作物件都可以正常執行。 ```csharp= class Program { static void Main(string[] args) { IMeasure square = new Square(); LengthMeasure(square, 10); Circle circle = new Circle(); LengthMeasure(circle, 10); Console.ReadKey(); } static void LengthMeasure(IMeasure measure, double len) { measure.Length(len); } } ``` > 參考資料 * [介面](http://www.kangting.tw/2018/11/blog-post_27.html) * [介面多型設計](http://www.kangting.tw/2018/11/blog-post_30.html) * [小山的 C# 教學-Interface](https://www.youtube.com/watch?v=ODrhZ1v63-s&list=WL&index=7)
×
Sign in
Email
Password
Forgot password
or
Sign in via Google
Sign in via Facebook
Sign in via X(Twitter)
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
Continue with a different method
New to HackMD?
Sign up
By signing in, you agree to our
terms of service
.