当前位置: 代码网 > it编程>编程语言>Asp.net > C#中的协变与逆变方式

C#中的协变与逆变方式

2024年12月17日 Asp.net 我要评论
前言在 c# 中,协变(covariance)和逆变(contravariance)是两个重要的概念,主要用于处理泛型类型参数的可变性。这两个概念允许在泛型类型之间进行更灵活的转换,提高了代码的可重用

前言

在 c# 中,协变(covariance)和逆变(contravariance)是两个重要的概念,主要用于处理泛型类型参数的可变性。

这两个概念允许在泛型类型之间进行更灵活的转换,提高了代码的可重用性和灵活性。

协变

(一)定义与概念

协变允许将一个派生类型的泛型参数转换为基类型的泛型参数。

简单来说,如果有两个泛型接口ifoo和ibar,并且ifoo的泛型参数t是协变的,那么如果a是b的派生类,就可以将ifoo转换为ifoo

(二)使用场景与示例

示例一:泛型接口中的协变

interface ifoo<out t>
{
    t getvalue();
}

假设我们有以下接口定义:

这里的out关键字表示t是协变的。现在我们可以这样使用这个接口:

class animal {}
class dog : animal {}

class program
{
    static void main()
    {
        ifoo<dog> dogfoo = new dogfoo();
        ifoo<animal> animalfoo = dogfoo; // 协变转换允许

        animal animal = animalfoo.getvalue();
    }
}

class dogfoo : ifoo<dog>
{
    public dog getvalue()
    {
        return new dog();
    }
}

在这个例子中,由于ifoo的t是协变的,我们可以将ifoo转换为ifoo。

这是因为dog是animal的派生类,并且getvalue方法只返回t类型的值,不会对其进行修改,所以这种转换是安全的。

示例二:委托中的协变

delegate t func<out t>();

class program
{
    static void main()
    {
        func<dog> dogfunc = getdog;
        func<animal> animalfunc = dogfunc; // 协变转换允许

        animal animal = animalfunc();
    }

    static dog getdog()
    {
        return new dog();
    }
}

c# 中的委托也可以支持协变。

例如:

这里的委托func<out t>t是协变的,所以可以将func<dog>转换为func<animal>

逆变

(一)定义与概念

逆变允许将一个基类型的泛型参数转换为派生类型的泛型参数。

如果有两个泛型接口ifoo<t>ibar<t>,并且ifoo<t>的泛型参数t是逆变的,那么如果ab的派生类,就可以将ifoo<b>转换为ifoo<a>

(二)使用场景与示例

示例一:泛型接口中的逆变

考虑以下接口定义:

interface ibar<in t>
{
    void setvalue(t value);
}

这里的in关键字表示t是逆变的。

现在我们可以这样使用这个接口:

class animal {}
class dog : animal {}

class program
{
    static void main()
    {
        ibar<animal> animalbar = new animalbar();
        ibar<dog> dogbar = animalbar; // 逆变转换允许

        dogbar.setvalue(new dog());
    }
}

class animalbar : ibar<animal>
{
    public void setvalue(animal value) {}
}

在这个例子中,由于ibar的t是逆变的,我们可以将ibar转换为ibar。

这是因为setvalue方法接受一个t类型的参数,并且dog是animal的派生类,所以可以将dog类型的参数传递给接受animal类型参数的方法,这种转换是安全的。

示例二:委托中的逆变

delegate void action<in t>(t value);

class program
{
    static void main()
    {
        action<animal> animalaction = setanimal;
        action<dog> dogaction = animalaction; // 逆变转换允许

        dogaction(new dog());
    }

    static void setanimal(animal value) {}
}

委托也可以支持逆变。

例如:

这里的委托action<in t>t是逆变的,所以可以将action<animal>转换为action<dog>

协变与逆变的限制与注意事项

(一)泛型类型参数的限制

对于协变的泛型类型参数,只能在接口或委托中作为返回值类型出现,不能作为方法的参数类型。

例如,以下代码是不合法的:

interface ifoo<out t>
{
    void dosomething(t value); // 错误,协变类型不能作为参数
}

对于逆变的泛型类型参数,只能在接口或委托中作为方法的参数类型出现,不能作为返回值类型。

例如,以下代码是不合法的:

interface ibar<in t>
{
    t getvalue(); // 错误,逆变类型不能作为返回值
}

(二)数组的协变与逆变

c#中的数组在一定程度上支持协变,但这种协变是不安全的。

例如:

animal[] animals = new dog[10]; // 编译通过,但运行时可能会抛出异常
animals[0] = new cat(); // 如果这里是一个 cat 对象,运行时会抛出异常

与数组不同,泛型类型的协变和逆变是安全的,因为编译器会在编译时进行类型检查,确保转换的安全性。

(三)可变性的范围

协变和逆变只适用于引用类型,对于值类型是不适用的。

例如,以下代码是不合法的:

interface ifoo<out t> where t : struct // 错误,协变不能用于值类型
{
    t getvalue();
}

总结

协变和逆变是 c#中强大的概念,可以提高代码的灵活性和可重用性。

通过正确使用协变和逆变,可以在泛型类型之间进行安全的转换,使得代码更加简洁和易于维护。

然而,在使用协变和逆变时,需要注意类型参数的限制和安全性,以避免潜在的运行时错误

以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com