美文网首页.Net基础系列
泛型协变与抗变(二)

泛型协变与抗变(二)

作者: 小世界的野孩子 | 来源:发表于2019-08-07 17:27 被阅读0次

前言

在.NET 4之前,泛型接口是不变的。.NET 4通过协变和抗变为泛型接口和泛型委托添加了一个重要的扩展。协变和抗变指对参数和返回值的类型进行转换。

我们来看下到底什么是协变什么是抗变:
  如果某个返回的类型可以由其基类替换,那么这个类型就是支持协变的
  如果某个参数类型可以由其派生类替换,那么这个类型就是支持逆变(抗变)的。

函数的类型转换

在理解协变与抗变之前,我们看下面这个例子:

  class Program
     { public static string Tmain(object o)
        { return "aaa";
        } static void Main(string[] args)
        { string a = "aaa"; object b = Tmain(a);
        }

     }

我们仔细看下这个传值和返回。注意其中发现了两次隐式转换。

1、向函数传值的时候 参数a从string类型转换成object类型

2、最后接收返回值的时候b由string类型转换成object类型

我们在返回函数来看。

1、 String Tmain(object o) 可以转换成string Tmain(string o)

2、 String Tmain(string o) 可以转换成 object Tmain(string o)

在这里,也就是说函数输入的时候输入类型可以从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)传入参数类型可以由其派生类所代替的时候,就是支持抗变(逆变)的。

夫学须静也,才须学也,非学无以广才,非志无以成学-------诸葛亮

相关文章

  • 泛型协变与抗变(二)

    前言 在.NET 4之前,泛型接口是不变的。.NET 4通过协变和抗变为泛型接口和泛型委托添加了一个重要的扩展。协...

  • 泛型编程中的型变

    在泛型编程中,经常会提到型变。型变分为两种:协变与逆变。协变covariant表示与泛型参数T的变化相同,而逆变c...

  • Kotlin学习笔记 - 泛型

    1. 基本用法 2. 型变 型变包括 协变、逆变、不变 三种: 协变:泛型类型与实参的继承关系相同 逆变:泛型类型...

  • Scala 泛型协变与泛型边界

    代码准备 泛型协变 泛型协变、逆变、不变是指拥有泛型的类在声明和赋值时的对应关系。 协变:声明时泛型是父类,赋值时...

  • Scala 通俗易懂 ---- 协变、逆变、不变

    协变、逆变、不变 Scala 语言中协变、逆变、不变是指拥有泛型的类型,在声明和赋值时的对应关系 协变:声明时泛型...

  • JAVA泛型与类型安全

    1. 基础泛型 2. 协变与逆变与不变 协变 简单来说即: Java中的数组是协变的 逆变与协变相对,逆转了类型关...

  • Java协变和逆变

    泛型的协变与逆变 协变与逆变用来描述类型转换(type transformation)后的继承关系,其定义如下:如...

  • Java 泛型与通配符

    参考地址:《Java 泛型,你了解类型擦除吗?》 《Java中的逆变与协变》 《java 泛型中 T、E .....

  • Kotlin中的协变、逆变和不变

    共1083字,阅读需要2分钟 泛型实参的继承关系对泛型类型的影响 协变:泛型类型与实参的继承关系相同 逆变:泛型类...

  • Objective-C 泛型 协变 逆变

    为什么要使用泛型 如何使用泛型 限制泛型 协变 逆变 为什么要使用泛型 在使用NSArray, NSSet, NS...

网友评论

    本文标题:泛型协变与抗变(二)

    本文链接:https://www.haomeiwen.com/subject/mtjadctx.html