10分鐘淺談泛型協變與逆變

来源:https://www.cnblogs.com/wangbaicheng1477865665/archive/2018/03/02/OutIn.html
-Advertisement-
Play Games

首先聲明,本文寫的有點粗糙,只讓你瞭解什麼是協變和逆變,沒有深入研究,根據這些年的工作經驗,發現我們在開發過程中,很少會自己去寫逆變和協變,因為自從net 4.0 (Framework 3.0) 以後,.net 就為我們提供了 定義好的逆變與協變。我們只要會使用就可以。協變和逆變都是在泛型中使用的。 ...


首先聲明,本文寫的有點粗糙,只讓你瞭解什麼是協變和逆變,沒有深入研究,根據這些年的工作經驗,發現我們在開發過程中,很少會自己去寫逆變和協變,因為自從net 4.0 (Framework 3.0) 以後,.net 就為我們提供了 定義好的逆變與協變。我們只要會使用就可以。協變和逆變都是在泛型中使用的。

  • 什麼是逆變與協變呢

 

可變性是以一種類型安全的方式,將一個對象當做另一個對象來使用。如果不能將一個類型替換為另一個類型,那麼這個類型就稱之為:不變數。協變和逆變是兩個相互對立的概念:

 

  • 如果某個返回的類型可以由其派生類型替換,那麼這個類型就是支持協變
  • 如果某個參數類型可以由其基類替換,那麼這個類型就是支持逆變的。

 

看起來你有點繞,我們先準備個“”鳥”類,在準備一個“麻雀”類,讓麻雀繼承鳥類,一起看代碼研究

 

  /// <summary>
    ////// </summary>
    public class Bird
    {
        public int Id { get; set; }
    }
    /// <summary>
    /// 麻雀
    /// </summary>
    public class Sparrow : Bird
    {
        public string Name { get; set; }
    }

 

 我們分別取實例化這個類,發現程式是能編譯通過的。

Bird bird1 = new Bird();
Bird bird2 = new Sparrow();
Sparrow sparrow1 = new Sparrow();

  //Sparrow sparrow2 = new Bird();//這個是編譯不通過的,違反了繼承性。

但是我們放在集合中,去實例化,是無法通過的

List<Bird> birdList1 = new List<Bird>();

//List<Bird> birdList2 = new List<Sparrow>();//不是父子關係,沒有繼承關係
//一群麻雀一定是一群鳥

 那麼我們如何去實現在泛型中的繼承性呢??這就引入了協變和逆變得概念,為了保證類型的安全,C#編譯器對使用了 out 和 in 關鍵字的泛型參數添加了一些限制:

  • 支持協變(out)的類型參數只能用在輸出位置:函數返回值、屬性的get訪問器以及委托參數的某些位置
  • 支持逆變(in)的類型參數只能用在輸入位置:方法參數或委托參數的某些位置中出現。
  • 協變

  我們來看下Net  “System.Collections.Generic”命名空間下的IEnumerable泛型 介面,會發現他的泛型參數使用了out 

現在我們使用下 IEnumerable  介面來進行一下上述實力,會發現,我們的泛型有了繼承關係。

IEnumerable<Bird> birdList1 = new List<Bird>();

IEnumerable<Bird> birdList2 = new List<Sparrow>();//協變
//一群麻雀一定是一群鳥

下麵我們來自己定義一個協變泛型介面ICustomerListOut<Out T>,讓 CustomerListOut 泛型類繼承CustomerListOut<Out T> 泛型介面。

代碼如下

    /// <summary>
    /// out 協變 只能是返回結果
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public interface ICustomerListOut<out T>
    {
        T Get();

       // void Show(T t);//T不能作為傳入參數
    }

    /// <summary>
    /// 類沒有協變逆變
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class CustomerListOut<T> : ICustomerListOut<T>
    {
        public T Get()
        {
            return default(T);
        }

        public void Show(T t)
        {

        }
    }

 我們會發現,在泛型斜變的時候,泛型不能作為方法的參數。我們用自己定義的泛型介面和泛型類進行實例化試試,我們會發現編譯通過

ICustomerListOut<Bird> customerList1 = new CustomerListOut<Bird>();//這是能編譯的
ICustomerListOut<Bird> customerList2 = new CustomerListOut<Sparrow>();//這也是能編譯的,在泛型中,子類指向父類,我們稱為協變

到這裡協變我們就學完了,協變就是讓我們的泛型有了子父級的關係。本文開始的時候,協變和逆變,是在C# 4.0 以後才有的,那C# 4.0以前我們是怎麼寫的呢,那個時候沒有協變?

老版本的寫法

  List<Bird> birdList3 = new List<Sparrow>().Select(c => (Bird)c).ToList();//4.0以前的寫法

等學完逆變,本文列出C# 4.0 以後的版本 中framework 已經定義好的協變、逆變 泛型介面,泛型類,泛型委托。

  • 逆變

 剛纔我們學習了泛型參數用out 去修飾,餃子協變,現在來學習下逆變,逆變是使用in來修飾的

這裡就是Net 4.0 給我們提供的逆變寫法

我們自己寫一個逆變的介面  ICustomerListIn<in T> ,在寫一個逆變的 泛型類 CustomerListIn<T>:ICustomerListIn<T> ,代碼如下

    /// <summary>
    /// 逆變
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public interface ICustomerListIn<in T>
    {
        //T Get();//不能作為返回值

        void Show(T t);
    }

    public class CustomerListIn<T> : ICustomerListIn<T>
    {
        public T Get()
        {
            return default(T);
        }

        public void Show(T t)
        {
        }
    }

 

 逆變的泛型參數是不能作為泛型方法的返回值的,我們來看下實例化鳥類,和麻雀類,看好使不好使。

ICustomerListIn<Sparrow> customerList2 = new CustomerListIn<Sparrow>();
ICustomerListIn<Sparrow> customerList1 = new CustomerListIn<Bird>();//父類指向子類,我們稱為逆變

ICustomerListIn<Bird> birdList1 = new CustomerListIn<Bird>();
birdList1.Show(new Sparrow());
birdList1.Show(new Bird());

Action<Sparrow> act = new Action<Bird>((Bird i) => { });

 到此我們就完全學完了逆變與協變

 

  • 總結

逆變與協變只能放在泛型介面和泛型委托的泛型參數裡面,

在泛型中out修飾泛型稱為協變,協變(covariant  修飾返回值 ,協變的原理是把子類指向父類的關係,拿到泛型中。
 在泛型中in 修飾泛型稱為逆變, 逆變(contravariant )修飾傳入參數,逆變的原理是把父類指向子類的關係,拿到泛型中。

  • NET 中自帶的斜變逆變泛型

 序號  類別  名稱
 1  介面  IEnumerable<out T> 
 2 委托  Action<in T>
3 委托 Func<out TResult>
 4  介面 IReadOnlyList<out T> 
 5  介面 IReadOnlyCollection<out T>
     

 各位朋友,如果誰還知道,請留言告知

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 簡介 IMapControl介面最重要的是包含IMap屬性,還提供另外的屬性用於:管理一般的外觀顯示,管理圖層,載入地圖文檔以及跟蹤在界面上顯示的圖形。 小知識點:一般一個介面的最新實現是 Default ,如果又多個實現,是通過數字區分,數字越大,其版本也越新。 屬性介紹 1. IActiveVi ...
  • 目錄 xBIM WeXplorer 簡要介紹 xBIM WeXplorer xViewer 基本應用 xBIM WeXplorer xViewer 瀏覽器檢查 xBIM WeXplorer xViewer的導航,相機、剖切、隱藏 等操作 xBIM WeXplorer 設置模型顏色 xBIM 綜合使用 ...
  • https://www.cnblogs.com/xishuai/p/asp-net-5-owin-katana.html http://wiki.jikexueyuan.com/project/think-in-asp-net-mvc/chapter-eight.html http://wiki.j ...
  • 目的 因為某些原因需要將存放在 Google Chrome 內的書簽導出到本地,所幸 Google Chrome 提供了導出書簽的功能。 分析 首先在 Google Chrome 瀏覽器當中輸入 來到書簽管理頁面,找到最右側的三個點,選擇導出書簽,導出的文件是一個 HTML 文件,裡面包含了所有書簽 ...
  • Power Shell管理Office參考http://www.mamicode.com/info-detail-494553.html C#調用Power Shell 參考 https://www.cnblogs.com/chenkai/archive/2010/11/09/1872471.htm ...
  • 前面文章介紹了ASP.NET MVC中的模型綁定和驗證功能,本著ASP.NET MVC沒有魔法的精神,本章內容將從代碼的角度對ASP.NET MVC如何完成模型的綁定和驗證進行分析,已瞭解其原理。 本文的主要內容有: ● ModelBinder ● ValuePrivoder ● ModelMeta ...
  • 利用vs創建一個MVC項目後,一般的預設啟動頁是根目錄下-->Controllers-->HomeController-->Index這個方法對應的頁面。 我先說下創建Areas的流程: 但是我們的controller一般都會建立很多,這樣我們就會想建個文件夾按照業務或者其他的分類方式把這麼多文件放 ...
  • 經常用到的一個知識點,每次用到就去百度一下,今天又用到了(又跑去擺渡了),為了防止用完就忘,用到就搜,今天自己記錄一下。 如何根據分隔符(比如逗號),將List<string>泛型集合合併成一個string字元串?以往的開發中,都是使用迴圈的方式來拼接成字元串,不僅要寫更多的代碼不說,還會消耗更多的 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...