? 在C#8.0中,針對介面引入了一項新特性,就是可以指定默認實作,方便對已有實作進行擴展,也對面向Android和Swift的Api進行互操作提供了可能性,下面我們來看看該特性的的概念、規則與示例代碼,
一、什么是默認實作
顧名思義,默認實作就是介面中的成員可以進行實作,并作為該成員的一個默認實作,在以后,在實作該介面的時候,如果實作了該介面的成員,則會被覆寫默認實作,否則、它的實作依然會使用介面中定義的默認實作,
二、主要應用場景:
在不破壞影響已有實作的情況下,可以添加新成員,這解決了在第三方已經大量使用了的介面上進行擴展帶來問題的痛點,
三、規則與限制:
1. 支持的成員:方法、屬性、索引器、 及各種靜態成員,不支持實體欄位、實體事件、自動屬性、實體構造和解構式
2. 支持修飾符:private, protected, internal, public, virtual, abstract, sealed, static, extern, and partial.
3. 默認訪問級別為public,可以顯式指定,也可以不指定,
4. 除過sealed和private修飾的方法體之外,其他帶有方法體的成員默認都是virtural成員
5. 介面中的默認實作只屬于該介面和繼承它的子介面,但不能被它的實作繼承,所以只能通過介面變數呼叫,除非介面的實作中進行了再次實作,
6. 多層次繼承的介面,呼叫最接近實作的介面的默認實作,也就是“層次最接近、最新實作最近、同級的new比overrided更接近”,
7. 在類中實作并覆寫介面中的成員,無需用new和override關鍵字,與介面的實作機制是保持一致,無需任何修改或操作,
四、實作舉例:
1. 先定義一個介面IFlyable,代碼如下:
public interface IFlyable { //支持const常量 public const int MAX_SPEED = 200; const int MIN_SPEED = 0; //默認public,可以省略 public const string SPEED_UOM = "m/s"; private static readonly Dictionary<string, string> nameDic; //支持靜態建構式,不支持實體建構式 static IFlyable() { nameDic = new Dictionary<string, string>() { {nameof(MAX_SPEED),MAX_SPEED.ToString()}, {nameof(MIN_SPEED),MIN_SPEED.ToString()} }; } //支持索引器,但是其中的變數也只能是靜態變數, string this[string key] { get { string tmp; if (nameDic.ContainsKey(key)) { tmp = nameDic[key]; } else { tmp = string.Empty; } return tmp; } } int Speed { get;} //默認為public和virtual,所以此處的virtual和public可有可無 public void Initialize() { var defaultSpeed = AverageSpeed(); Initialize(defaultSpeed); WriteLine($"{nameof(IFlyable) + "." + nameof(Initialize)} at default {defaultSpeed} {SPEED_UOM}"); } // 私有帶有方法體的成員是允許存在的 private int AverageSpeed() { return (MAX_SPEED + MIN_SPEED) / 2; } void Initialize(int speed); //默認為public和virtual,所以此處的virtual和public可有可無 void Fly() { WriteLine($"{nameof(IFlyable) + "." + nameof(Fly)}"); } }
2. 再定義一個IAnimal介面:
public interface IAnimal { //默認為public和virtual,可以顯式指出該成員時virtual void SayHello() { WriteLine($"{nameof(IAnimal) + "." + nameof(SayHello)}"); } void Walk() { WriteLine($"{nameof(IAnimal) + "." + nameof(Walk)}"); } }
3. 定義一個IFlyableAnimal介面,繼承自前兩個介面
public interface IFlyableAnimal:IAnimal,IFlyable { public new const int MAX_SPEED = 300; //重寫IAnimal的SayHello, void IAnimal.SayHello() { WriteLine($"override {nameof(IFlyableAnimal) + "." + nameof(SayHello)} "); } //因為IFlyableAnimal介面繼承了IAnimal的介面,加new關鍵字來隱藏父類的繼承的SayHello,可以不加,但會有警告, public new void SayHello() { WriteLine($"new {nameof(IFlyableAnimal) + "." + nameof(SayHello)}"); } //因為IFlyableAnimal介面繼承了IFlyable的介面,介面繼承的默認實作是無法用override關鍵字的 public void Walk() { WriteLine($"new {nameof(IFlyableAnimal) + "." + nameof(Walk)}"); } }
4. 定義一個類Sparrow,來實作介面IFlyableAnimal
public class Sparrow : IFlyableAnimal { //實作IFlyable中的Speed介面 public int Speed { get; private set; } //實作IFlyable中的Initialize(int speed)介面 public void Initialize(int speed) { this.Speed = speed; WriteLine($"{nameof(Sparrow) + "." + nameof(Initialize)} at {Speed} {IFlyable.SPEED_UOM}"); } //實作并覆寫介面中的SayHello,無需用new和override關鍵字,與介面的實作機制是保持一致,無需任何修改或操作, public virtual void SayHello() { // 注意的使用IFlyableAnimal.SPEED_UOM,類只能實作介面,不能繼承介面的默認實作,但是介面可以繼承父介面的默認實作 WriteLine($"{nameof(Sparrow) + "." + nameof(SayHello)} at {Speed} {IFlyableAnimal.SPEED_UOM}"); } }
5. 對前面的定義進行呼叫
static void Main(string[] args) { Sparrow bird = new Sparrow(); bird.Initialize(98); //Sparrow中實作并覆寫了Initialize,所以可以直接用類呼叫 bird.SayHello();//Sparrow中實作并覆寫了,所以可以直接用類呼叫 //bird.Fly(); Fly不可訪問,因為Bird沒有實作也不會繼承介面中的實作,所以不擁有Fly方法, //IFlyableAnimal 繼承了IAnimal和IFlyable的默認實作,通過該變數呼叫SayHello和Fly方法 IFlyableAnimal flyableAnimal = bird; flyableAnimal.SayHello(); flyableAnimal.Fly(); //IFlyableAnimal繼承自IAnimal和IFlyable,而Sparrow類又繼承自了IFlyableAnimal,所以可以用IAnimal和IFlyable變數呼叫 IAnimal animal = bird; animal.SayHello(); IFlyable flyable = bird; flyable.Initialize(); flyable.Fly(); Monster monster = new Monster(); IAlien alien = monster; //alien.Fly();//編譯器無法分清是'IBird.Fly()' 還是 'IInsect.Fly()' } //輸出: //Sparrow.Initialize at 98 m/s //Sparrow.SayHello at 98 m/s //Sparrow.SayHello at 98 m/s //IFlyable.Fly //Sparrow.SayHello at 98 m/s //Sparrow.Initialize at 100 m/s //IFlyable.Initialize at default 100 m/s //IFlyable.Fly
五、多層次繼承產生的問題
因為介面時可以多重繼承的,這樣就會出現類似C++里產生菱形繼承問題,如下圖所示,IBird和IInsect都繼承了IFlyable,而IAlien又同時繼承IBird和IInsert兩個介面,并做了新的實作,這時,他們就構成了一個菱形或者鉆石形狀,這時候,實作了IAlien的Monster類,就會無法分清改用哪個Fly

代碼如下:
public interface IBird : IFlyable { void Fly() { WriteLine($"{nameof(IBird) + "." + nameof(Fly)}"); } } public interface IInsect : IFlyable { void Fly() { WriteLine($"{nameof(IInsect) + "." + nameof(Fly)}"); } } public interface IAlien : IBird, IInsect { } public class Monster : IAlien { public int Speed { get; private set; } = 1000; public void Initialize(int speed) { this.Speed = speed; } }
下面呼叫陳述句alien.Fly就會導致混淆,編譯器無法分清此Fly到底是IBird.Fly() 還是IInsect.Fly():
static void Main(string[] args) { Monster monster = new Monster(); IAlien alien = monster; //alien.Fly();//編譯器無法分清是'IBird.Fly()' 還是 'IInsect.Fly()' }
對于這種問題的解決方案,是要么通過更為具體的介面(IBird或IInsect)來呼叫相應成員,要么在Monster類中實作Fly,再通過類來呼叫,
六、總結
C#8.0的介面默認實作對于軟體的功能的擴展提供了比較大的靈活性,同時,也引入了一些規則,使得掌握其的成本增加,這里,我盡力求對其一些規則等做出了總結,并展現了示例予以說明,肯定又不足指出,希望大家指正,
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/71341.html
標籤:C#
上一篇:opencv +數字識別
