服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C# - 详析C#的协变和逆变

详析C#的协变和逆变

2022-12-16 12:06RyzenAdorer C#

这篇文章主要介绍了详析C#的协变和逆变,在引用类型系统时,协变、逆变和不变性具有如下定义。 这些示例假定一个名为 Base 的基类和一个名为 Derived的派生类,更多内容请需要的小伙伴参考下面文章内容

前言:

在引用类型系统时,协变、逆变和不变性具有如下定义。 这些示例假定一个名为 Base 的基类和一个名为 Derived的派生类。

Covariance

使你能够使用比原始指定的类型派生程度更大的类型。

你可以将 IEnumerable 的实例分配给 IEnumerable 类型的变量。

Contravariance

使你能够使用比原始指定的类型更泛型(派生程度更小)的类型。

你可以将 Action 的实例分配给 Action 类型的变量。

Invariance

表示只能使用最初指定的类型。 固定泛型类型参数既不是协变,也不是逆变。

你无法将 List 的实例分配给 List 类型的变量,反之亦然。

以上来自于官方文档对协变、逆变、不变性的解释

为啥C#需要协变和逆变?

我们首先来看一段代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
class FooBase{ }
 
class Foo : FooBase 
{
 
}
 
var foo = new Foo();
FooBase fooBase = foo;
 
//以下代码在.NET 4.0之前是不被支持的
IEnumerable<Foo> foo = new List<Foo>();
IEnumerable<FooBase> fooBase = foo;

因此,在这里实际上可以回答,C#的协变和逆变就是主要有两种目的:

  • 兼容性:.NET2.0就推出了泛型,而从.NET 2.0到.NET 3.5期间不支持对泛型接口中的占位符T支持隐式转换,因此在.NET4.0推出协变和逆变
  • 为了支持更广泛的隐式类型的转换,在这里就是在泛型体系中支持

在C#中,目前只有泛型接口和泛型委托可以支持协变和逆变,

协变(Covariance)

内置的泛型协变接口,IEnumeratorIQuerableIGrouping

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  public interface IEnumerable<out T> : IEnumerable
    {
        new IEnumerator<T> GetEnumerator();
    }
 
 
    public interface IQueryable<out T> : IEnumerable<T>, IEnumerable, IQueryable
    {
 
    }
 
 
    public interface IGrouping<out TKey, out TElement> : IEnumerable<TElement>, IEnumerable
    {
      TKey Key { get; }
    }

因此这段代码在.NET4.0及以上版本将不会编译报错:

IEnumerable<Foo> foo = new List<Foo>();
IEnumerable<FooBase> fooBase = foo;

实际上,对于协变,有下面的约束,否则则会在编译时报错:

  • 泛型参数占位符以out关键子标识,并且占位符T只能用于只读属性、方法或者委托的返回值,out简而易懂,就是输出的意思
  • 当要进行类型转换,占位符T要转换的目标类型也必须是其基类,上述例子则是Foo隐式转为FooBase

逆变(Contravariance)

内置的泛型逆变委托Action、Func、Predicate,内置的泛型逆变接口IComparable<T>、IEquatable<T>:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  public delegate void Action<in T>(T obj);
 
  public delegate TResult Func<in T, out TResult>(T arg);
 
  public delegate bool Predicate<in T>(T obj);
 
 
  public interface IComparable<in T>
  {
    int CompareTo(T? other);
  }
 
  public interface IEquatable<T>
  {
    bool Equals(T? other);
  }

而逆变的用法则是这样:

?
1
2
3
Action<FooBase> fooBaseAction = new Action<FooBase>((a)=>Console.WriteLine(a));
 
Action<Foo> fooAction = fooBaseAction;

而对于逆变,则跟协变相反,有下面的约束,否则也是编译时报错:

要想标识为逆变,应该是要在占位符T前标识in,只能用于只写属性、方法或者委托的输入参数
当要进行类型转换,占位符T要转换的目标类型也必须是其子类,上述例子则是FooBase转为Foo
总结#
协变和逆变只对泛型委托和泛型接口有效,对普通的泛型类和泛型方法无效
协变和逆变的类型必须是引用类型,因为值类型不具备继承性,因此类型转换存在不兼容性
泛型接口和泛型委托可同时存在协变和逆变的类型参数,即占位符T

到此这篇关于详析C#的协变和逆变的文章就介绍到这了,更多相关C#的协变和逆变内容请搜索服务器之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持服务器之家!

原文链接:https://www.cnblogs.com/ryzen/p/15771954.html

延伸 · 阅读

精彩推荐
  • C#unity实现延迟回调工具

    unity实现延迟回调工具

    这篇文章主要为大家详细介绍了unity实现延迟回调工具,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    骚年狠冲洞9442022-12-05
  • C#C#实现类似jQuery的方法连缀功能

    C#实现类似jQuery的方法连缀功能

    这篇文章主要介绍了C#实现类似jQuery的方法连缀功能,可以简化语句,使代码变得清晰简单,感兴趣的小伙伴们可以参考一下...

    JackWang-CUMT4762021-11-02
  • C#WinForm实现窗体最大化并遮盖任务栏的方法

    WinForm实现窗体最大化并遮盖任务栏的方法

    这篇文章主要介绍了WinForm实现窗体最大化并遮盖任务栏的方法,涉及C#实现WinForm窗体全屏显示的实现及调用技巧,具有一定参考借鉴价值,需要的朋友可以参考...

    我心依旧4902021-10-22
  • C#C#中WinForm控件的拖动和缩放的实现代码

    C#中WinForm控件的拖动和缩放的实现代码

    本篇文章主要介绍了C#中WinForm控件的拖动和缩放的实现代码,C# WinForm控件的拖动和缩放是个很有用的功能,有兴趣的可以了解一下。...

    十日十乞00110372021-12-22
  • C#c# HashSet的扩容机制需要注意的

    c# HashSet的扩容机制需要注意的

    这篇文章主要介绍了c# HashSet的扩容机制需要注意的两个地方,文中讲解非常细致,代码帮助大家更好的理解和学习,感兴趣的朋友可以了解下...

    一线码农4912022-09-15
  • C#c# 用ffmpeg从视频中截图

    c# 用ffmpeg从视频中截图

    这篇文章主要介绍了c# 用ffmpeg从视频中截图的方法,帮助大家更好的理解和学习使用c#,感兴趣的朋友可以了解下...

    UP技术控11152022-11-08
  • C#C# WPF如何反射加载Geometry几何图形数据图标

    C# WPF如何反射加载Geometry几何图形数据图标

    这篇文章主要介绍了C# WPF如何反射加载Geometry几何图形数据图标,帮助大家更好的理解和学习使用c#,感兴趣的朋友可以了解下...

    Stay6276582022-11-09
  • C#轻松学习C#的抽象类

    轻松学习C#的抽象类

    轻松学习C#的抽象类,对C#的抽象类感兴趣的朋友可以参考本篇文章,帮助大家更灵活的运用C#的抽象类。...

    丿木呈广予口贝10032021-11-04