泛型协变与抗变(二)

前言

  在.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#知识

 

  

相关文章
相关标签/搜索