主頁 > .NET開發 > C# 8.0 新特性之二:介面默認實作

C# 8.0 新特性之二:介面默認實作

2020-09-18 06:19:22 .NET開發

?      在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 +數字識別

下一篇:C#設計模式學習筆記:(17)中介者模式

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • WebAPI簡介

    Web體系結構: 有三個核心:資源(resource),URL(統一資源識別符號)和表示 他們的關系是這樣的:一個資源由一個URL進行標識,HTTP客戶端使用URL定位資源,表示是從資源回傳資料,媒體型別是資源回傳的資料格式。 接下來我們說下HTTP. HTTP協議的系統是一種無狀態的方式,使用請求/ ......

    uj5u.com 2020-09-09 22:07:47 more
  • asp.net core 3.1 入口:Program.cs中的Main函式

    本文分析Program.cs 中Main()函式中代碼的運行順序分析asp.net core程式的啟動,重點不是剖析原始碼,而是理清程式開始時執行的順序。到呼叫了哪些實體,哪些法方。asp.net core 3.1 的程式入口在專案Program.cs檔案里,如下。ususing System; us ......

    uj5u.com 2020-09-09 22:07:49 more
  • asp.net網站作為websocket服務端的應用該如何寫

    最近被websocket的一個問題困擾了很久,有一個需求是在web網站中搭建websocket服務。客戶端通過網頁與服務器建立連接,然后服務器根據ip給客戶端網頁發送資訊。 其實,這個需求并不難,只是剛開始對websocket的內容不太了解。上網搜索了一下,有通過asp.net core 實作的、有 ......

    uj5u.com 2020-09-09 22:08:02 more
  • ASP.NET 開源匯入匯出庫Magicodes.IE Docker中使用

    Magicodes.IE在Docker中使用 更新歷史 2019.02.13 【Nuget】版本更新到2.0.2 【匯入】修復單列匯入的Bug,單元測驗“OneColumnImporter_Test”。問題見(https://github.com/dotnetcore/Magicodes.IE/is ......

    uj5u.com 2020-09-09 22:08:05 more
  • 在webform中使用ajax

    如果你用過Asp.net webform, 說明你也算是.NET 開發的老兵了。WEBform應該是2011 2013左右,當時還用visual studio 2005、 visual studio 2008。后來基本都用的是MVC。 如果是新開發的專案,估計沒人會用webform技術。但是有些舊版 ......

    uj5u.com 2020-09-09 22:08:50 more
  • iis添加asp.net網站,訪問提示:由于擴展配置問題而無法提供您請求的

    今天在iis服務器配置asp.net網站,遇到一個問題,記錄一下: 問題:由于擴展配置問題而無法提供您請求的頁面。如果該頁面是腳本,請添加處理程式。如果應下載檔案,請添加 MIME 映射。 WindowServer2012服務器,添加角色安裝完.netframework和iis之后,運行aspx頁面 ......

    uj5u.com 2020-09-09 22:10:00 more
  • WebAPI-處理架構

    帶著問題去思考,大家好! 問題1:HTTP請求和回傳相應的HTTP回應資訊之間發生了什么? 1:首先是最底層,托管層,位于WebAPI和底層HTTP堆疊之間 2:其次是 訊息處理程式管道層,這里比如日志和快取。OWIN的參考是將訊息處理程式管道的一些功能下移到堆疊下端的OWIN中間件了。 3:控制器處理 ......

    uj5u.com 2020-09-09 22:11:13 more
  • 微信門戶開發框架-使用指導說明書

    微信門戶應用管理系統,采用基于 MVC + Bootstrap + Ajax + Enterprise Library的技術路線,界面層采用Boostrap + Metronic組合的前端框架,資料訪問層支持Oracle、SQLServer、MySQL、PostgreSQL等資料庫。框架以MVC5,... ......

    uj5u.com 2020-09-09 22:15:18 more
  • WebAPI-HTTP編程模型

    帶著問題去思考,大家好!它是什么?它包含什么?它能干什么? 訊息 HTTP編程模型的核心就是訊息抽象,表示為:HttPRequestMessage,HttpResponseMessage.用于客戶端和服務端之間交換請求和回應訊息。 HttpMethod類包含了一組靜態屬性: private stat ......

    uj5u.com 2020-09-09 22:15:23 more
  • 部署WebApi隨筆

    一、跨域 NuGet參考Microsoft.AspNet.WebApi.Cors WebApiConfig.cs中配置: // Web API 配置和服務 config.EnableCors(new EnableCorsAttribute("*", "*", "*")); 二、清除默認回傳XML格式 ......

    uj5u.com 2020-09-09 22:15:48 more
最新发布
  • C#多執行緒學習(二) 如何操縱一個執行緒

    <a href="https://www.cnblogs.com/x-zhi/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2943582/20220801082530.png" alt="" /></...

    uj5u.com 2023-04-19 09:17:20 more
  • C#多執行緒學習(二) 如何操縱一個執行緒

    C#多執行緒學習(二) 如何操縱一個執行緒 執行緒學習第一篇:C#多執行緒學習(一) 多執行緒的相關概念 下面我們就動手來創建一個執行緒,使用Thread類創建執行緒時,只需提供執行緒入口即可。(執行緒入口使程式知道該讓這個執行緒干什么事) 在C#中,執行緒入口是通過ThreadStart代理(delegate)來提供的 ......

    uj5u.com 2023-04-19 09:16:49 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    <a href="https://www.cnblogs.com/huangxincheng/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/214741/20200614104537.png" alt="" /&g...

    uj5u.com 2023-04-18 08:39:04 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    一:背景 1. 講故事 前段時間協助訓練營里的一位朋友分析了一個程式卡死的問題,回過頭來看這個案例比較經典,這篇稍微整理一下供后來者少踩坑吧。 二:WinDbg 分析 1. 為什么會卡死 因為是表單程式,理所當然就是看主執行緒此時正在做什么? 可以用 ~0s ; k 看一下便知。 0:000> k # ......

    uj5u.com 2023-04-18 08:33:10 more
  • SignalR, No Connection with that ID,IIS

    <a href="https://www.cnblogs.com/smartstar/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/u36196.jpg" alt="" /></a>...

    uj5u.com 2023-03-30 17:21:52 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:15:33 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:13:31 more
  • C#遍歷指定檔案夾中所有檔案的3種方法

    <a href="https://www.cnblogs.com/xbhp/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/957602/20230310105611.png" alt="" /></a&...

    uj5u.com 2023-03-27 14:46:55 more
  • C#/VB.NET:如何將PDF轉為PDF/A

    <a href="https://www.cnblogs.com/Carina-baby/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2859233/20220427162558.png" alt="" />...

    uj5u.com 2023-03-27 14:46:35 more
  • 武裝你的WEBAPI-OData聚合查詢

    <a href="https://www.cnblogs.com/podolski/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/616093/20140323000327.png" alt="" /><...

    uj5u.com 2023-03-27 14:46:16 more