你了解C#的协变和逆变吗,看完这篇就懂了

C#的协变和逆变是在面向对象里面的类型系统中的概念。在C# 2.0之前,这两个概念是不存在的,开发者只能通过强制类型转换来满足某些需求。在C# 2.0之后,引入了这两个概念,通过它们可以更加安全地进行类型转换,同时也提升了代码的可读性。

一、协变:

协变指的是能够将一个派生类的变量赋值给基类的变量,或者能够将一个方法的返回值类型声明为基类的类型。它的形态如下:

interface IAnimal { }
class Dog : IAnimal { }
class Cat : IAnimal { }

// 可以将Dog类型的变量赋值给IAnimal类型的变量
IAnimal animal = new Dog();
// 可以将返回值类型声明为IAnimal类型
IAnimal GetAnimal() { return new Dog(); }

二、逆变:

逆变指的是能够将一个基类的变量赋值给派生类的变量,或者可以将一个方法的参数类型声明为派生类的类型。它的形态如下:

interface IAnimal { }
class Dog : IAnimal { }
class Cat : IAnimal { }

// 可以将IAnimal类型的变量赋值给Dog类型的变量
Dog dog = (Dog)new IAnimal();
// 可以将参数类型声明为Dog类型
void AddDog(Dog dog) { }

示例一:协变

interface ILogger<out T> { 
    T Log();
}

class DbLogger<T> : ILogger<T> {
    public T Log() { 
        Console.WriteLine("DbLogger Log: " + typeof(T).ToString());
        return default(T); 
    }
}

class FileLogger<T> : ILogger<T> {
    public T Log() { 
        Console.WriteLine("FileLogger Log: " + typeof(T).ToString());
        return default(T); 
    }
}

ILogger<object> logger1 = new DbLogger<object>();
ILogger<object> logger2 = new FileLogger<object>();

上面的例子中,ILogger接口有一个占位符类型T,用于表示的具体的类型是什么。DbLogger和FileLogger都有一个Log方法,返回值类型为T。我们通过ILogger等同于ILogger来声明一个协变的泛型接口,然后分别声明一个DbLogger和FileLogger并赋值给ILogger类型的logger1和logger2变量来实现协变。

示例二:逆变

delegate T Func<T>();
delegate void Set<in T>(T value);

class Fruit { }
class Apple : Fruit { }

class Msg { }
class WarningMsg : Msg { }

void SetFruit(Set<Fruit> setFruit) { }
void GetApple(Func<Apple> getApple) { }

Set<Apple> setApple = a => { Console.WriteLine("Set Apple"); };
SetFruit(setApple); // 逆变

Func<Fruit> getFruit = () => { Console.WriteLine("Get Fruit"); return new Apple(); };
GetApple(getFruit); // 逆变

上面的例子中,我们先声明了两个delegate,Func和Set。它们分别有一个类型参数T,并且Set的类型参数类型是逆变的,Func的类型参数类型是协变的。然后我们声明了两个类Fruit和Apple,并且Apple是Fruit的子类。同样地,我们还声明了两个类Msg和WarningMsg,并且WarningMsg是Msg的子类。

接着,我们定义了两个方法SetFruit和GetApple。SetFruit要求传入一个Set类型的参数,而GetApple要求传入一个Func类型的参数。然后我们声明一个Set类型的变量setApple,它实现了Set。接着我们调用SetFruit方法,将setApple作为参数传入,这里setApple是在逆变的情况下实现了Set,可以看做是将Set类型的变量赋值给了Set类型的变量。

同理,我们声明一个Func类型的变量getFruit,实现了Func。接着我们调用GetApple方法,将getFruit作为参数传入,这里getFruit是在协变的情况下实现了Func,可以看做是将Func类型的变量赋值给了Func类型的变量。

总结:

协变和逆变可以让我们使用更加灵活的泛型类型,提高程序的安全性和可读性。协变是将派生类赋值给基类或者将返回值类型声明为基类,逆变是将基类赋值给派生类或者将参数类型声明为派生类。在编程中,我们可以根据需求来采用协变和逆变的形式,灵活运用泛型类型,来更加高效地编写代码。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:你了解C#的协变和逆变吗,看完这篇就懂了 - Python技术站

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

相关文章

  • C#操作Windows服务类System.ServiceProcess.ServiceBase

    C#操作Windows服务需要使用System.ServiceProcess.ServiceBase类。下面是使用这个类的完整攻略。 ServiceBase类 ServiceBase类是用于开发Windows服务的基类,它提供了操作Windows服务的方法和属性。 安装/卸载服务 安装Windows服务需要使用InstallUtil.exe工具,在Visua…

    C# 2023年6月7日
    00
  • C# WinForm实现Win7 Aero透明效果代码

    下面是详细讲解“C# WinForm实现Win7 Aero透明效果代码”的完整攻略: 1. 实现方法 要实现Win7 Aero透明效果,需要在WinForm程序中使用DWM(Desktop Window Manager)API。具体实现步骤如下: 1.1 定义API函数 在C#中使用DWM API需要使用P/Invoke方式。首先需要定义DWM API的函数…

    C# 2023年6月8日
    00
  • ASP.NET(C#)中遍历所有控件

    遍历所有控件可以使用递归方法,递归遍历每个控件,并递归遍历控件中的所有子控件。 以下是C#中遍历所有控件的完整攻略: 步骤1:创建递归方法 创建递归方法,并在其中遍历每个控件: private void TraverseControls(Control control) { foreach (Control childControl in control.C…

    C# 2023年6月3日
    00
  • 武装你的WEBAPI-OData与DTO

    本文属于OData系列文章 Intro 前面写了很多有关OData使用的文章,很多读者会有疑问,直接将实体对象暴露给最终用户会不会有风险?$expand在默认配置的情况下,数据会不会有泄露风险? 答案是肯定的,由于OData的特性,提供给我们便捷同时也会带来一些风险。很多地方推荐使用DTO模式来隔离实体类与最终用户使用到类的关系,从而解决以上两个问题,ODa…

    C# 2023年5月8日
    00
  • C#难点逐个击破(6):C#数据类型与.net framework数据类型

    C#难点逐个击破(6):C#数据类型与.net framework数据类型 什么是数据类型 在程序开发中,数据类型是对数据进行分类的一种方式。不同的数据类型可以存储不同种类的数据,并且对每种数据类型进行不同的操作。 在C#中,数据类型可以分为两类:值类型和引用类型。其中,值类型的数据是以值形式存储,而引用类型的数据则是以引用形式存储。 .NET Framew…

    C# 2023年5月31日
    00
  • C#实现统计字数功能的方法

    下面是“C#实现统计字数的功能”的完整攻略: 一、需求分析 在进行编码之前,我们需要先分析需求,明确要实现的功能。在这个任务中,我们需要实现统计一段文本中包含的字符数和单词数的功能。 字符数的统计比较简单,只需要计算文本长度即可。而对单词数的统计涉及到对文本内容的分词和统计,需要采用一定的算法实现。 二、实现步骤 1. 统计字符数 要统计字符数,首先需要获取…

    C# 2023年6月1日
    00
  • .Net Core读取文件时中文乱码问题的解决方法分享

    .NET Core读取文件时中文乱码问题的解决方法分享 在.NET Core中,读取文件时中文乱码是一个常见的问题。在本攻略中,我们将详细讲解.NET Core读取文件时中文乱码问题的解决方法,并提供两个示例说明。 步骤一:使用正确的编码方式读取文件 在.NET Core中,您需要使用正确的编码方式读取文件,以避免中文乱码问题。以下是使用正确的编码方式读取文…

    C# 2023年5月17日
    00
  • C#类继承中构造函数的执行序列示例详解

    以下是“C#类继承中构造函数的执行序列示例详解”的完整攻略。 1. 构造函数的执行序列 在C#中,当一个派生类(子类)的对象被创建时,其继承树上所有基类(父类)中的构造函数也会被调用。构造函数的调用顺序如下: 调用基类的构造函数 执行派生类自身的构造函数 下面通过示例来详细讲解: public class BaseClass { public BaseCla…

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