首页 > 其他分享 >协变(Covariance) 和 逆变(Contravariance)

协变(Covariance) 和 逆变(Contravariance)

时间:2024-09-28 13:45:21浏览次数:8  
标签:逆变 Covariance class Contravariance Animal 协变 泛型 public

协变(Covariance)逆变(Contravariance) 是面向对象编程中关于类型系统的一种概念,主要用于处理泛型类型、接口或委托的继承和类型转换问题。它们决定了在继承结构中,泛型参数如何与类型或接口的继承关系保持一致或相反。

1. 协变(Covariance)

协变指的是,当你有一个泛型类型 G<T>,如果 TDerivedTBase 的子类,那么 G<TDerived> 可以被看作是 G<TBase> 的子类。这意味着协变允许你在一个方法、接口或委托中返回派生类型,而类型参数是更一般的基类。

C# 中协变的例子

在 C# 中,可以使用 out 关键字来声明协变,通常用于返回类型的泛型接口或委托。

// 声明接口 IProducer<out T> 支持协变
public interface IProducer<out T>
{
    T Produce();
}

public class Animal { }
public class Dog : Animal { }

public class DogProducer : IProducer<Dog>
{
    public Dog Produce() => new Dog();
}

public class Program
{
    public static void Main()
    {
        IProducer<Animal> animalProducer = new DogProducer();
        Animal animal = animalProducer.Produce();  // 协变允许泛型类型转换
    }
}

在上面的例子中:

  • DogProducer 实现了 IProducer<Dog>,但可以赋值给 IProducer<Animal>,因为 DogAnimal 的子类,协变允许这个类型转换。
  • IProducer<T> 使用了 out,这表明它是协变的。

协变的场景

协变一般用于方法的返回类型,即子类实例可以作为基类的返回结果,主要用于只输出数据的场景,比如:

  • 迭代器
  • 泛型委托(如 Func<T>

2. 逆变(Contravariance)

逆变是指当你有一个泛型类型 G<T>,如果 TDerivedTBase 的子类,那么 G<TBase> 可以被看作是 G<TDerived> 的子类。逆变允许使用基类来替代派生类。

C# 中逆变的例子

在 C# 中,可以使用 in 关键字来声明逆变,通常用于泛型参数作为方法参数输入的接口或委托。

// 声明接口 IConsumer<in T> 支持逆变
public interface IConsumer<in T>
{
    void Consume(T item);
}

public class Animal { }
public class Dog : Animal { }

public class AnimalConsumer : IConsumer<Animal>
{
    public void Consume(Animal animal)
    {
        Console.WriteLine("Consuming an animal.");
    }
}

public class Program
{
    public static void Main()
    {
        IConsumer<Dog> dogConsumer = new AnimalConsumer();
        dogConsumer.Consume(new Dog());  // 逆变允许泛型类型转换
    }
}

在上面的例子中:

  • AnimalConsumer 实现了 IConsumer<Animal>,但可以赋值给 IConsumer<Dog>,因为 AnimalDog 的基类,逆变允许这个类型转换。
  • IConsumer<T> 使用了 in,这表明它是逆变的。

逆变的场景

逆变一般用于方法的参数类型,允许基类参数接受派生类对象,主要用于只输入数据的场景,比如:

  • 事件处理器
  • 泛型委托(如 Action<T>

3. 协变与逆变的总结

  • 协变(Covariance,使用 out:用于返回类型允许子类替代基类。适用于只读场景(如返回值)。
  • 逆变(Contravariance,使用 in:用于参数类型允许基类替代子类。适用于只写场景(如方法参数)。

图解协变和逆变

  • 协变:G<TDerived> 可以当作 G<TBase>

    • DogAnimal 的子类,因此 IProducer<Dog> 可以赋值给 IProducer<Animal>
  • 逆变:G<TBase> 可以当作 G<TDerived>

    • AnimalDog 的父类,因此 IConsumer<Animal> 可以赋值给 IConsumer<Dog>

4. C++中的协变和逆变

C++ 本身没有像 C# 中那样直接支持协变和逆变的关键字,但在继承和指针/引用类型转换中,也有类似的概念。C++ 支持协变返回类型:派生类可以覆盖基类的函数,且返回派生类的对象,而不必返回基类对象。

class Base {
public:
    virtual Base* create() {
        return new Base();
    }
};

class Derived : public Base {
public:
    Derived* create() override {  // 协变返回类型
        return new Derived();
    }
};

在这个例子中,Derived 类覆盖了 Base 类的 create 方法,并且返回 Derived*,这是协变返回类型的一个例子。

  • 协变逆变 是用于泛型类型的继承转换的概念,协变用于输出类型,逆变用于输入类型。
  • 在 C# 中,使用 outin 关键字来分别实现协变和逆变。
  • 在 C++ 中,虽然没有直接的关键字,但可以通过返回类型协变等机制实现类似的效果。

标签:逆变,Covariance,class,Contravariance,Animal,协变,泛型,public
From: https://www.cnblogs.com/linxmouse/p/18437583

相关文章

  • STAT3006/7305 STAT3006/7305  covariance matrix
    STAT3006/7305Tutorial 11. Assumeis bivariate normal with meanand covariance matrixDeriveanexpressionforthemarginaldistributionofX1 .2. Forthe aboveproblem, derive anexpressionfor the conditional distribution of X1 fromthe......
  • 晕晕的逆变协变
    逆变的原理当我们有一个方法或接口接受一个基类的实例作为参数时,我们希望也能接受该基类的派生类的实例。逆变使得我们能够将AppleAction作为IAction<Fruit>使用,因为我们知道Apple类型的实例也是Fruit类型的实例。总结逆变(in)使得我们能够将一个处理派生类型的......
  • 【C#开发】C#的协变和逆变
    当时第一次学到协变和逆变的时候印象不是很深,不是很能理解。结果很快想起在刷leetcode题的时候遇到关于这个问题的情况。问题的出现就比如力扣第15题三数之和他的返回值是IList<IList<int>>代表意思类似二维数组。我第一反应就是先声明一个List<List<int>>res的变......
  • 泛型中的协变和逆变
    协变和逆变都是术语,前者指能够使用比原始指定的派生类型的派生程度更大(更具体的)的类型,后者指能够使用比原始指定的派生类型的派生程度更小(不太具体的)的类型。泛型类型参数支持协变和逆变,可在分配和使用泛型类型方面提供更大的灵活性。微软官方解释:https://learn.microsoft.com/z......
  • 协变与逆变
    在C#中父类可以直接转子类,叫做协变子类转父类需要添加强制转换,叫做逆变publicclassPerson{}publicclassStudent:Person{}varp=newPerson();p=newStudent();//协变是父类转子类,可以直接转vart=newStudent();t=(Student)new......
  • 协变返回类型(covariant return type)
    协变返回类型(covariantreturntype)C++中的协变返回类型(covariantreturntype)是指派生类(子类)中的虚函数返回类型可以是基类(父类)中虚函数返回类型的子类。这个特性使得在派生类中可以返回更具体的类型,而不违反了虚函数的约定。在C++11中,如果派生类的虚函数覆盖基类的虚函数,并......
  • c#学习笔记----------------------------协变和逆变
    协变和逆变协变和逆变能够实现数组类型、委托类型和泛型类型参数的隐式引用转换。协变保留分配兼容性,逆变则与之相反。协变以下代码演示支持协变与不支持协变的泛型和数组的区别//泛型委托publicdelegateTMyFuncA<T>();//不支持逆变与协变......
  • java~类型的逆变和协变
    在Java中,泛型的逆变(contravariance)和协变(covariance)是涉及到泛型类型转换时的两个重要概念。协变(Covariance)协变指的是子类型对象可以赋值给父类型引用的情况。在泛型中,协变表示如果B是A的子类,那么List<B>就是List<A>的子类。这意味着你可以将List<B>赋值给List<A>......
  • 07c#协变逆变
    namespace协变逆变{classProgram{staticvoidMain(string[]args){//问题1:为什么会有协变逆变?//首先,由里氏替换我们知道,子类可以安全的赋值给父类。//(不熟悉可以会议六大设计原则//solid://s——单一职责//o——开闭原则//l——里氏替换//I——接口隔离//d——迪......
  • covariance matrix in signal processing
    cross-covarianceInthecaseofcomplexrandomvariables,thecovarianceisdefinedslightlydifferentlycomparedtorealrandomvariables.Forcomplexrandomvariables(Z_1)and(Z_2),thecovarianceisdefinedas:\[\text{Cov}(Z_1,Z_2)=E[(Z_1-......