C#泛型的逆变协变之个人理解

下面是关于C#泛型的逆变协变的个人理解和相关示例:

什么是泛型逆变和协变?

在C#中,泛型指的是具有参数化类型的类和方法。逆变和协变是泛型中的一种特殊的概念,它们分别指泛型类型参数的赋值方式和约束条件。

泛型逆变(contravariance)指的是泛型类、泛型委托等类型参数的类型参数能够赋值给其父类型参数,或者其父类型参数的类型参数(即父类型的类型参数)能够赋值给泛型类、泛型委托等类型参数。换句话说,泛型逆变就是子类型可以隐式转化为父类型的过程。

而泛型协变(covariance)则正好相反,它指的是泛型类、泛型委托等类型参数的类型参数能够赋值给其子类型参数,或者其子类型参数的类型参数(即子类型的类型参数)能够赋值给泛型类、泛型委托等类型参数。换句话说,泛型协变就是父类型可以隐式转化为子类型的过程。

示例1:逆变

下面是一个逆变的示例:

public delegate void MyDelegate<in T>(T arg);

其中,in T表明泛型委托类型的参数是逆变的,即T只能出现在方法参数列表中,并且只能作为输入参数,不能作为输出参数。比如:

public void Test(string str)
{
    Console.WriteLine("Test: " + str);
}
public void Test2(object obj)
{
    Console.WriteLine("Test2: " + obj.ToString());
}

MyDelegate<string> myDelegate1 = Test;
MyDelegate<object> myDelegate2 = myDelegate1;
myDelegate2("hello world");

示例中,我们先定义了两个方法,一个接收参数类型为string,一个接收参数类型为object。然后定义了泛型委托类型MyDelegate<T>,并指明T是逆变的。接着,我们实例化了一个泛型委托类型的对象myDelegate1,它的参数类型是string,并将它赋值给了另一个泛型委托类型的对象myDelegate2,它的参数类型是object。由于MyDelegate<T>的参数类型是逆变的,因此myDelegate2可以接收myDelegate1的参数类型,即string类型的参数。最后,我们通过myDelegate2调用方法Test,并传入参数hello world,这时打印出的结果为:

Test: hello world

示例2:协变

下面是一个协变的示例:

interface IMyInterface<out T>
{
    T GetObject();
}

class MyClass<T> : IMyInterface<T>
{
    T obj;

    public MyClass(T o)
    {
        obj = o;
    }

    public T GetObject()
    {
        return obj;
    }
}

IMyInterface<object> iobj = new MyClass<string>("Hello World");
Console.WriteLine(iobj.GetObject());

示例中,我们定义了一个泛型接口IMyInterface<out T>,其中out T表明泛型参数是协变的,即T只能作为返回类型,不能作为输入参数类型。具体实现接口的类MyClass<T>中,我们通过构造函数接收一个泛型类型的参数,并将它赋值给类的成员变量obj。同时,GetObject方法将返回成员变量obj

接着,我们定义了一个IMyInterface<object>类型的变量iobj,并将其实例化为MyClass<string>类型的对象。由于IMyInterface<T>的泛型参数是协变的,因此可以将MyClass<string>类型的对象赋值给IMyInterface<object>类型的变量iobj。然后通过iobj调用MyClass<string>类型对象的GetObject方法,此时打印出的结果为:

Hello World

总结

泛型逆变和协变是C#中泛型的特殊概念,它们让我们能够更加灵活地使用泛型类型参数,实现了泛型类型之间的隐式转换,提高了代码的可读性、可维护性和可扩展性。有了这些特性,我们可以更加方便地编写对于类型参数完全一样但参数顺序不同的代码,以及可以在具体类型尚未确定的时候就开始编写相应的泛型代码,同时减少了类型转换的代码,提高了代码的运行效率。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C#泛型的逆变协变之个人理解 - Python技术站

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

相关文章

  • asp.net 未能加载文件或程序集“XXX”或它的某一个依赖项。试图加载格式不正确的程序。

    在ASP.NET开发中,经常会遇到“未能加载文件或程序集‘XXX’或它的某一个依赖项。试图加载格式不正确的程序”的错误提示。这个错误信息通常是由程序集加载失败所引起的,而这个问题的背后通常有两个原因:第一个原因是程序集本身缺失或损坏,第二个原因是引用的程序集发生变化。 如果遇到这个错误,可以按照以下步骤来解决: 1.重新编译项目:首先,尝试重新编译项目。打开…

    C# 2023年5月15日
    00
  • C# CSV文件读写的实现

    C# CSV文件读写的实现攻略 CSV(Comma Separated Values)是一种常用的文件格式,以逗号作为分隔符并且每行数据独占一行,适合于数据交换和数据存储。下面是实现CSV文件读写的步骤。 步骤1: 引入CSV文件格式处理库 C#中有很多好用的CSV文件格式处理库可以选择,比如:CsvHelper、LINQ to CSV等,我这里以CsvHe…

    C# 2023年6月1日
    00
  • 使用HttpClient增删改查ASP.NET Web API服务

    以下是“使用HttpClient增删改查ASP.NET WebAPI服务”的完整攻略: 什么是HttpClient HttpClient是.NET Framework中的一个类,它提供一组用于发送请求和接收HTTP的方法。我们可以使用它来消费Web API服务。 使用HttpClient增删改查.NET WebAPI服务 以下是使用HttpClient增删改…

    C# 2023年5月12日
    00
  • c#多线程网络聊天程序代码分享(服务器端和客户端)

    C#多线程网络聊天程序代码分享(服务器端和客户端) 介绍 本文所分享的是使用C#编写的多线程网络聊天程序的源代码,包括服务器端和客户端代码。网络聊天程序可以实现在不同计算机之间进行即时聊天的功能,多线程可以提升程序的并发性和性能,同时使用C#编写可以大大简化代码编写过程。 实现流程 服务器端程序编写 服务器端程序的主要作用是接受用户请求,并与客户端进行通讯。…

    C# 2023年6月6日
    00
  • asp.net中一个linq分页实现代码

    针对“asp.net中一个linq分页实现代码”的完整攻略,我将分为以下几个步骤进行详细讲解。 步骤一:定义分页方法 首先,在asp.net中,我们可以通过自定义分页方法来实现分页功能。这里我们可以采用linq语句来获取指定页码的数据,以下是一个简单的分页方法示例: public IQueryable<T> GetPagedData<T&g…

    C# 2023年5月31日
    00
  • C#调用QQ_Mail发送邮件实例代码两例

    关于C#调用QQ_Mail发送邮件实例代码,以下是完整攻略。 1. 准备工作 在使用C#编写代码发送QQ_Mail邮件前,你需要完成以下准备工作: 获取SMTP服务器地址和端口号,可以在QQ邮箱的设置中找到。 获得登录QQ邮箱时使用的邮箱地址和密码。 2. 示例1:使用System.Net.Mail.SmtpClient发送邮件 使用System.Net.M…

    C# 2023年5月15日
    00
  • C#生成互不相同随机数的实现方法

    下面是“C#生成互不相同随机数的实现方法”的攻略。 1. 问题背景 在某些情况下,我们需要在程序中生成一组互不相同的随机数。例如,需要为多个用户分配不同的抽奖号码或者生成一组随机的测试数据。 2. 方案思路 实现这个需求的一种思路是:每次使用随机数时,从一个预设的随机数池中选取一个未使用过的数作为结果。这个思路的优点是可以确保生成的随机数互不相同,缺点则是需…

    C# 2023年6月7日
    00
  • 用上这几种.NET EF Core性能调优,查询性能飙升

    1、避免在循环中进行查询操作: 避免在循环中进行查询操作,可以将查询结果缓存到内存中,然后对内存中的数据进行操作,可以提高性能。这种方式适合集合数据量少的数据,否则利大于弊。 // 不建议的方式:在循环中进行查询操作 foreach (var item in itemList) { var result = context.Items.FirstOrDefa…

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