泛型協變與抗變(二)

前言

  在.NET 4以前,泛型接口是不變的。.NET 4經過協變和抗變爲泛型接口和泛型委託添加了一個重要的擴展。協變和抗變指對參數和返回值的類型進行轉換。html

  咱們來看下到底什麼是協變什麼是抗變:
  若是某個返回的類型能夠由其基類替換,那麼這個類型就是支持協變的
  若是某個參數類型能夠由其派生類替換,那麼這個類型就是支持逆變(抗變)的。編程

函數的類型轉換

  在理解協變與抗變以前,咱們看下面這個例子:c#

  class Program
     {
        public static string Tmain(object o)
        {
            return "aaa";
        }

        static void Main(string[] args)
        {
            string a = "aaa";
            object b = Tmain(a);
        }

     }

 

  咱們仔細看下這個傳值和返回。注意其中發現了兩次隱式轉換。函數

    一、向函數傳值的時候 參數a從string類型轉換成object類型學習

    二、最後接收返回值的時候b由string類型轉換成object類型spa

  咱們在返回函數來看。code

    一、 String Tmain(object o) 能夠轉換成string Tmain(string o)htm

    二、 String Tmain(string o) 能夠轉換成 object Tmain(string o)blog

  在這裏,也就是說函數輸入的時候輸入類型能夠從object轉換成string。基類-派生類接口

  在函數輸出時,函數的輸出類型(返回類型)從string轉換成object。派生類-基類。

  這裏就比較接近泛型接口的協變和抗變的概念了。咱們再看咱們開頭的概念

 

  若是某個返回的類型能夠由其基類替換,那麼這個類型就是支持協變的
  若是某個參數類型能夠由其派生類替換,那麼這個類型就是支持逆變(抗變)的。

 

理解泛型接口的協變和抗變(in、out)

  咱們下面來看看泛型接口的協變及抗變的例子:

  首先咱們看下協變,在C#高級編程(第十一版)中指出,若是泛型類型用out關鍵字標註,泛型接口就是協變的。這也就意味着返回類型只能是T。

    /// <summary>
    /// 標識out,意味着返回類型只能是T
    /// </summary>
    /// <typeparam name="T"></typeparam>
    interface Itest<out T>
    {
        T Tmain(object value);
    }

 
    public class Test : Itest<string>
    {
        public string Tmain(object value)
        {
            return value.ToString();
        }
 }

 

咱們調用時:

 static void Main(string[] args)
        {
            Itest<string> itest = new Test();
            Itest<object> itestObj = itest;
        }

 

  在這裏,咱們最後接收其返回值的時候,理應由string類型進行接收的,可是這裏咱們能夠修改,由其基類object類型進行替換。也就是在某個返回類型能夠由其基類替換的時候,也就是支持協變了。注意其關鍵點。返回類型、由基類替換派生類。

 

 

  而後咱們再看看那抗變也可稱爲逆變。在C#高級編程中指出的概念:若是泛型類型用in關鍵字標註,泛型接口就是抗變的。這樣,接口只能把泛型類型T用做其方法的輸入。

  /// <summary>
    /// 標識in,意味着輸入類型只能是T
    /// </summary>
    /// <typeparam name="T"></typeparam>
    interface Itest<in T>
    {
        string Tmain(T value);
    }


    public class Test : Itest<object>
    {
        public string Tmain(object value)
        {
            return value.ToString();
        }
    }

 
    class Program
    {
        static void Main(string[] args)
        {
            Itest<object> itest = new Test();
            Itest<string> itestStr= itest;
        }
}

 

 

  這裏咱們看上面這個例子,其中返回類型已是固定的string類型了。而泛型接口中的泛型類型用來做爲參數傳遞了。咱們再看調用時,正常傳入object類型的參數,,可是咱們修改傳入參數類型爲string類型也是能夠的。也就是咱們在參入參數時,參數能夠由其派生類替換的話,那麼這個類也就是支持抗變(逆變)的。注意其中關鍵點。傳入參數,派生類替換基類。

總結

  其實在上述例子及其概念中,咱們能夠發現,泛型接口的協變及抗變,也就是將類型參數返回或者傳入的狀況,在這狀況下進行其類型的隱式轉換所遵循的規律。

    協變:(使用關鍵字out)返回類型能夠由其基類所替代的時候,就是支持協變的。

    抗變(逆變):(使用關鍵字in)傳入參數類型能夠由其派生類所代替的時候,就是支持抗變(逆變)的。

 

  夫學須也,才須學也,非學無以廣才,非志無以成學-------諸葛亮

 


 

                      c#基礎知識詳解系列

 

  歡迎你們掃描下方二維碼,和我一塊兒學習更多的C#知識

 

  

相關文章
相關標籤/搜索