Contents

协变和逆变

协变和逆变

什么是协变和逆变

  • 协变
    • 因为 里氏替换原则 父类容器可以装子类对象
    • 所以子类转换成父类 例如 string 转换成object
    • 这样的转换 是协变 是和谐的转换
  • 逆变
    • 因为 里氏替换原则 父类容器可以装子类对象 但是子类容器不可以装父类对象
    • 所以父类转换成子类 比如object转换成string
    • 这样的转换 是逆变 是不和谐的

协变和逆变的用法

  • 协变和逆变是用来修饰泛型的
  • 使用关键字out(协变)和in(逆变)
  • 用于在泛型中 修饰 泛型字母的
  • 只有泛型接口和泛型委托中可以使用

out修饰的泛型 使其只能作为返回值

//T只能作为返回值
delegate T TestOut<out T>();
delegate T TestOut<out T>(T value);//报错

//在泛型接口中使用
interfacet Test<out T>{
    T TestFun1();
    void TestFun2(T value);//报错
}

in修饰的泛型 使其只能作为参数

//T只能作为参数
delegate void TestIn<in T>(T value);
delegate T TestOIn<in T>(T value);//报错
//在泛型接口中使用
interfacet Test<in T>{
    void TestFun2(T value);
    T TestFun1();//报错
}

通过里氏替换原则 理解协变

class Fater{}
class Son:Fater{}

delegate T TestOut<out T>();
delegate T TestNoOut<T>();

TestNoOut<Son> tnos = ()=>{return new Son();}
TestNoOut<Fater> tnof = tnos;//报错

//out修饰 会去判断父子关系
TestOut<Son> tos = ()=>{return new Son();}
TestOut<Fater> tof = tos;
//返回值为父类的方法 将子类作为返回值返回
//父类装子类 协变
Fater f = tof();

//返回值为子类的方法 不可以将父类作为返回值返回
tos = tof//报错

通过里氏替换原则 理解逆变

class Fater{}
class Son:Fater{}

delegate void TestIn<in T>(T value);
delegate void TestNoIn<T>(T value);

TestNoIn<Fater> tnif = (value)=>{};
TestNoIn<Son> tnis = tnif;//报错

//in修饰 会去判断父子关系
TestIn<Fater> tif = (value)=>{};
TestIn<Son> tis = tif;
//将子类的对象  传入了参数为父类的方法里
//子类转父类 逆变
tis(new sun());

//父类对象 不可以传入 子类作为参数的方法里
tif = tis;//报错

协变和逆变有什么作用

  • 协变(out)和逆变(in)是修饰 泛型的
  • 协变 限定泛型只能作为 返回值
  • 逆变 限定泛型只能作为 参数
  • 协变:父类泛型委托容器 装 子类泛型泛型容器
    • 返回值是子类的委托 加到 返回值是为父类的委托中
  • 逆变:子类泛型泛型容器 装 父类泛型委托容器
    • 参数是父类的委托 加到 参数为子类的委托中