一文带你了解C#中的协变与逆变

一文带你了解C#中的协变与逆变

什么是协变与逆变

在程序设计中,经常需要对类进行继承和实现接口的操作。在这样的过程中,我们通常会遇到这样的问题:子类或者实现接口的类的泛型参数类型和父类的泛型参数类型不匹配。而“协变”和“逆变”就是解决这样的问题的方法。

协变和逆变是 C# 4.0 引入的两个关键技术,可以让我们更加灵活地使用泛型。在 C# 中,协变和逆变可以用于委托、接口和数组等这些数据结构。

协变

协变是指在保持类型安全的前提下,可以将从泛型类派生的子类对象作为基类对象使用。也就是说,对于一个泛型类型 T, 如果 T1 是另一个类型 T2 的子类,那么 T 就是 T 的子类。

我们可以理解一个泛型类的协变是这个泛型类泛型参数的类型可以隐式向上转换。例如:

public interface IAnimal { }

public class Animal { }

public class Dog : Animal, IAnimal { }

public interface ITest<out T> where T : IAnimal
{
    T Get();
}

public class Test<T> : ITest<T> where T : IAnimal
{
    public T Get()
    {
        return default(T);
    }
}

public class Program
{
    public static void Main(string[] args)
    {
        ITest<Animal> animalTest = new Test<Dog>(); //协变
    }
}

在上面的代码中,我们定义了一个 Test 类,实现了 ITest 接口,并且使用了 out 关键字限定了 T 的类型。其中,IAnimal 是 Animal 的子类,也就是说,Dog 类型是 Animal 的子类。

在 Main() 方法中,我们使用 Test() 对象来初始化 ITest 类型的 animalTest 对象,也就是说,我们成功地将一个泛型类型参数隐式转换为了一个更泛化的类型参数,这里就是把 Dog 类型转换为了 Animal 类型。

逆变

逆变与协变正好相反,是指在保持类型安全的前提下,可以将能够接受泛型类对象的类型更改为可以接受泛型类的基类。也就是说,对于一个泛型类型 T,如果 T1 是另一个类型 T2 的基类,那么 T 就是 T 的基类。

我们可以理解一个泛型类的逆变是这个泛型类泛型参数的类型可以隐式向下转换。例如:

public interface IAnimal { }

public class Animal { }

public class Dog : Animal, IAnimal { }

public interface ITest<in T> where T : Animal
{
    void Post(T animal);
}

public class Test<T> : ITest<T> where T : Animal
{
    public void Post(T animal)
    {
        //DO SOMETHING
    }
}

public class Program
{
    public static void Main(string[] args)
    {
        ITest<Dog> dogTest = new Test<Animal>(); //逆变
    }
}

在上面的代码中,我们定义了一个 Test 类,实现了 ITest 接口,并且使用了 in 关键字限定了 T 的类型。其中,IAnimal 是 Animal 的子类,也就是说,Dog 类型是 Animal 的子类。

在 Main() 方法中,我们使用 Test() 对象来初始化 ITest 类型的 dogTest 对象,也就是说,我们成功地将一个泛型类型参数隐式转换为了一个更具体的类型参数,这里就是把 Animal 类型转换为了 Dog 类型。

示例

协变示例:IEnumerable

IEnumerable 接口是 C# 中的一个经典协变示例。这个接口定义了一个能够枚举 T 类型元素的集合。如果这个集合类型是一个枚举器 TItem,那么,IEnumerable 就是 IEnumerable 的子类型。

下面是一个利用协变的例子:

public static void Main(string[] args)
{
    var dogs = new List<Dog>
    {
        new Dog(),
        new Dog()
    };

    IEnumerable<IAnimal> animals = dogs;
}

在这个例子中,我们把 List 类型赋值给了 IEnumerable 类型,因为 Dog 类型是 IAnimal 类型的子类型。

逆变示例:Func

Func 委托是 C# 中的一个经典逆变示例。这个委托返回值的类型是 TResult,输入参数的类型是 TArg。如果 TResult 是 TResult1 的子类型,而 TArg1 是 TArg 的父类型,那么 Func 就是 Func 的基类型。

下面是一个利用逆变的例子:

public static void Main(string[] args)
{
    Func<Animal, string> animalToString = animal => animal.ToString();

    Func<Dog, string> dogToString = animalToString;

    Console.WriteLine(dogToString(new Dog()));
}

在这个例子中,我们把一个 Func 委托类型隐式地转换为了 Func 委托类型,即把一个 Animal 类型转换为了 Dog 类型。这实现了逆变。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:一文带你了解C#中的协变与逆变 - Python技术站

(0)
上一篇 2023年5月15日
下一篇 2023年5月15日

相关文章

  • C#编程实现获取文件夹中所有文件的文件名

    下面是详细的攻略: 使用C#编程实现获取文件夹中所有文件的文件名 1. 打开Visual Studio创建新的控制台应用程序项目 以Visual Studio 2019为例,新建项目流程如下: 打开 Visual Studio。 选择“创建新项目”。 选择“控制台应用程序”。 可以选择使用.Net Framework或.Net Core,选择一个你习惯的就好…

    C# 2023年6月1日
    00
  • C# WPF上位机实现和下位机TCP通讯的方法

    下面是对于“C# WPF上位机实现和下位机TCP通讯的方法”的完整攻略: 1. 概述 要实现 C# WPF 上位机和下位机(例如单片机)之间的 TCP 通讯,可以分为以下三个步骤:1. 建立 TCP 连接2. 实现数据的发送和接收3. 关闭 TCP 连接 这三个步骤的具体实现细节将在下面讨论,示例将基于 C# 语言和 WPF 框架。 2. 建立 TCP 连接…

    C# 2023年5月15日
    00
  • c# 静态类的使用场景

    下文是关于”C# 静态类的使用场景”的完整攻略。 什么是 C# 静态类 在 C# 中,静态类(Static Class)是指不能被实例化的类,该类中的所有成员都必须是静态的。同时,静态类不能被继承,因此它不能有实例。静态类通常用于封装工具方法,使这些方法可以作为应用程序的公用工具使用。 C# 静态类的使用场景 静态类的主要作用是封装公用的工具方法,以方便其他…

    C# 2023年5月31日
    00
  • .NET 6 整合 Autofac 依赖注入容器

    前言 一行业务代码还没写,框架代码一大堆,不利于学习。常看到java的学习资料或博客,标题一般为《SpringBoot 整合 XXX》,所以仿照着写了《.NET 6 整合 Autofac 依赖注入容器》这样一个标题。以下是我自己的用法,可能不是最佳实践。 一. 引用包 NuGet搜索并安装:AutofacAutofac.Extensions.Dependen…

    C# 2023年5月3日
    00
  • C# String.Equals()方法: 比较两个字符串是否相等

    String.Equals()方法用于比较两个字符串对象的值是否相等,返回一个布尔值。该方法有多种重载形式,可以按照需要选择不同的形式使用。 下面详细讲解String.Equals()的作用和使用方法: 作用 String.Equals()方法用于比较两个字符串对象的值是否相等,返回一个布尔值。该方法可以用于比较任意两个字符串,包括空字符串,但需要注意的是,…

    C# 2023年4月19日
    00
  • 如何通过C#/VB.NET代码将PowerPoint转换为HTML

    利用PowerPoint可以很方便的呈现多媒体信息,且信息形式多媒体化,表现力强。但难免在某些情况下我们会需要将PowerPoint转换为HTML格式。因为HTML文档能独立于各种操作系统平台(如Unix,Windows等)。并且它可以加入图片、声音、动画、影视等内容,还能从一个文件跳转到另一个文件,与世界各地主机的文件连接。通过HTML可以表现出丰富多彩的…

    C# 2023年5月8日
    00
  • C#中OpenFileDialog和PictrueBox的用法分析

    C#中OpenFileDialog和PictureBox用法分析 OpenFileDialog和PictureBox的作用 OpenFileDialog是C#中的一个对话框控件,可以用于打开文件,并返回文件在文件系统中的完整路径。当需要在程序中加载图片时,可以使用PictureBox控件将图片显示出来。 OpenFileDialog的用法 在C#中打开Ope…

    C# 2023年5月15日
    00
  • .Net的GC垃圾回收原理及实现

    .NET的GC垃圾回收原理及实现 在.NET中,垃圾回收(GC)是一种自动内存管理机制,它负责在运行时自动释放不再使用的内存。在本攻略中,我们将详细讲解.NET的GC垃圾回收原理及实现,并提供两个示例说明。 垃圾回收原理 .NET的GC垃圾回收原理基于以下两个核心概念: 1. 引用计数 引用计数是一种内存管理技术,它通过计算对象的引用数来确定对象是否可以被释…

    C# 2023年5月17日
    00
合作推广
合作推广
分享本页
返回顶部