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

yizhihongxing

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#中TransactionScope的使用小结

    C#中TransactionScope的使用小结 1. 什么是TransactionScope TransactionScope是C#中一个用于管理事务的类,位于System.Transactions命名空间中。它可以让多条语句成为一个事务,从而保证在一个事务中,要么所有语句都执行成功,要么全部失败。 2. TransactionScope的使用方法 步骤1…

    C# 2023年5月15日
    00
  • c# dynamic的好处

    C#中的dynamic类型是.NET Framework 4.0的新增特性。使用dynamic类型可以让C#在编译前不进行类型检查,而是在运行时根据实际的数据类型动态地进行方法和属性调用,从而更加灵活和方便地处理一些类型不确定的数据处理场景。以下是C# dynamic类型的好处的详细讲解以及两条示例说明: 1. 灵活方便地处理任意类型数据 C#的强类型检查机…

    C# 2023年5月31日
    00
  • ListView用法中与滚动相关的需求实现

    ListView是Android中常用的控件之一,它可以显示多个数据项,使得用户可以通过上下滑动来浏览不同的数据,因此与滚动相关的需求是ListView中的重要部分,本文将对ListView的滚动相关的需求进行详细讲解。 ListView滚动相关的需求 ListView滚动相关的需求包括两种:滚动控制和滚动监听。 滚动控制需要实现以下需求: 滚动到指定位置 …

    C# 2023年6月6日
    00
  • 如何在ASP.NET Core应用程序运行Vue并且部署在IIS上详解

    如何在ASP.NET Core应用程序运行Vue并且部署在IIS上详解 在ASP.NET Core应用程序中运行Vue,可以使用Vue CLI创建Vue项目,并将Vue项目集成到ASP.NET Core应用程序中。在部署到IIS上时,需要将Vue项目打包为静态文件,并将其部署到IIS的静态文件目录中。 以下是在ASP.NET Core应用程序中运行Vue并部…

    C# 2023年5月16日
    00
  • WinForm中BackgroundWorker控件用法简单实例

    下面我将为您详细讲解 “WinForm中BackgroundWorker控件用法简单实例”的攻略。 背景介绍 BackgroundWorker 控件是一个可将耗时操作异步在后台运行的控件,它是一个轻量级的多线程组件,采用消息机制处理异步耗时操作完成后的回调。使用 BackgroundWorker 可以避免 UI 界面假死或者卡顿的情况,提升程序的用户体验。 …

    C# 2023年6月7日
    00
  • 在C#中List集合使用First()方法获取第一个元素的操作

    当我们使用C#中的List集合时,我们可能需要获取集合中的第一个元素。List提供了First()方法,可以快速地获取到第一个元素。下面是一份详细的攻略,包含了List集合的创建、添加、获取第一个元素等操作。 创建List集合 首先我们需要创建一个List集合。可以使用以下代码创建一个名为list的List集合。 List<string> lis…

    C# 2023年5月15日
    00
  • JavaScript基本数据类型及值类型和引用类型

    当我们编写JavaScript代码时,我们需要了解JavaScript的基本数据类型及值类型和引用类型。 JavaScript基本数据类型 JavaScript基本数据类型包括数字、字符串、布尔值、null、undefined和Symbol(ES6中新增)。它们是不可改变的,也就是说,一旦创建,我们无法对它们进行修改。 数字 JavaScript中的数字可以…

    C# 2023年5月15日
    00
  • Unity3D使用鼠标旋转缩放平移视角

    让我为您详细讲解一下“Unity3D使用鼠标旋转缩放平移视角”的完整攻略。 1.概述 在Unity3D中,使用鼠标旋转、缩放、平移视角,是非常常见和实用的操作。这种交互方式,有很多常见的应用场景,比如第三人称视角、自由视角、场景漫游、3D地图等等。在这篇攻略中,我将分享三种不同的示例,让您了解如何实现这些常见的交互操作。 2.鼠标旋转视角 以下是Unity3…

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