C#进阶-协变与逆变,2024年最新价值2000元的学习资源泄露

81 阅读5分钟
3、继承关系与类型转换

继承关系与类型转换是协变和逆变的基础。在协变的情况下,随着类型的派生程度增加,泛型类型参数的继承关系也得到了保持。而在逆变的情况下,派生程度更大的类型可以被隐式地转换为派生程度更小的类型。这些关系可以用公式表示如下:

  • 当 A ≤ B 时,若 f(x) 是逆变的,则 f(B) ≤ f(A) 成立。
  • 当 A ≤ B 时,若 f(x) 是协变的,则 f(A) ≤ f(B) 成立。

这些公式强调了在协变和逆变中,类型转换的方向是根据类型的继承关系而变化的。


二、逆变与协变的运用

1、泛型委托

a、协变

协变允许我们在泛型委托中进行父类到子类的隐式转换。请参考下面的示例:

public delegate T DelegateFuncA(); //支持协变 DelegateFuncA funcObject = null; DelegateFuncA funcString = null; funcObject = funcString; //协变

在这个例子中,DelegateFuncA 声明了一个支持协变的泛型委托。通过将 funcString 赋值给 funcObject,我们实现了从 stringobject 的协变转换。这种灵活性可以在一些场景中提高代码的可读性和可维护性。 

b、逆变

逆变允许我们在泛型委托中进行子类到父类的隐式转换。请参考下面的示例:

public delegate void DelegateFuncB(T param); //支持逆变 DelegateFuncB actionObject = null; DelegateFuncB actionString = null; actionString = actionObject; //逆变

在这个示例中,DelegateFuncB 是一个支持逆变的泛型委托。通过将 actionObject 赋值给 actionString,我们实现了从 objectstring 的逆变转换。逆变为我们提供了更多的灵活性,使得我们能够更容易地传递具有更具体类型的方法给接受更抽象类型的委托。


2、泛型接口

a、协变

协变在泛型接口中的应用同样是非常有意义的。请参考下面的示例:

public delegate T InterfaceFuncA(); //支持协变 InterfaceFuncA interfaceFuncObject = null; InterfaceFuncA interfaceFuncString = null; interfaceFuncObject = interfaceFuncString; //协变

在这个例子中,我们使用支持协变的泛型接口 InterfaceFuncA。通过将 interfaceFuncString 赋值给 interfaceFuncObject,我们实现了从 stringobject 的协变转换。 

b、逆变

逆变同样可以在泛型接口中发挥作用。请参考下面的示例:

public delegate void InterfaceFuncB(T param); //支持逆变 InterfaceFuncB interfaceFuncObjectB = null; InterfaceFuncB interfaceFuncStringB = null; interfaceFuncStringB = interfaceFuncObjectB; //逆变

在这个例子中,我们使用支持逆变的泛型接口 InterfaceFuncB。通过将 interfaceFuncObjectB 赋值给 interfaceFuncStringB,我们实现了从 objectstring 的逆变转换。


3、数组逆变

逆变同样可以在数组中发挥作用,使得派生程度更大的类型的数组能够隐式转换为派生程度更小的类型的数组。请参考下面的示例:

// 数组的逆变使派生程度更大的类型的数组能够隐式转换为派生程度更小的类型的数组。 object[] array = new string[10]; array[0] = 10;

在这个示例中,我们创建了一个 object 类型的数组,但实际上它指向的是一个 string 类型的数组。这是数组逆变的一个实际应用,使得我们能够更灵活地处理不同派生程度的数组类型。


三、逆变与协变的总结

C#中的逆变和协变是一组强大的泛型特性,它们为我们在处理泛型委托和泛型接口时提供了更灵活、更安全的类型转换机制。在面向对象的编程中,我们经常需要在不同层次的类之间进行转换,而逆变和协变正是为了在这些转换过程中提供便利。

协变,即子类到父类的转换,使得我们能够以一种更通用的方式使用类型。这对于泛型委托和泛型接口的设计非常有用,允许我们在一些场景中将泛型参数类型更灵活地指定为其基类型。这种灵活性为代码的可扩展性和可维护性带来了巨大的优势。

与协变相反,逆变提供了一种父类到子类的转换方式。这在某些场景下同样非常有用,允许我们将具有更具体类型的方法传递给接受更抽象类型的泛型委托或泛型接口。逆变为代码的复用性和可读性提供了更高的提升。

尽管逆变和协变的语法可能在初次接触时显得陌生,但通过实际项目的使用,你将逐渐体会到它们的价值。在日常编码中,多次应用这些特性,你会发现它们在提高代码的表达能力和逻辑清晰性方面发挥了关键作用。因此,鼓励大家在实践中多次运用逆变和协变,相信你会在这个过程中获得丰富的经验和感悟。逐渐地,你将更加熟练地运用这些强大的语言特性,提升你的C#编程技能。

img img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!