網站首頁 編程語言 正文
前言:
在引用類型系統時,協變、逆變和不變性具有如下定義。 這些示例假定一個名為 Base 的基類和一個名為 Derived的派生類。
Covariance
使你能夠使用比原始指定的類型派生程度更大的類型。
你可以將 IEnumerable
的實例分配給 IEnumerable
類型的變量。
Contravariance
使你能夠使用比原始指定的類型更泛型(派生程度更小)的類型。
你可以將 Action
的實例分配給 Action
類型的變量。
Invariance
表示只能使用最初指定的類型。 固定泛型類型參數既不是協變,也不是逆變。
你無法將 List
的實例分配給 List 類型的變量,反之亦然。
以上來自于官方文檔對協變、逆變、不變性的解釋
為啥C#需要協變和逆變?
我們首先來看一段代碼:
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)
內置的泛型協變接口,IEnumerator
、IQuerable
、IGrouping
:?
? 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>:
? 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); ? }
而逆變的用法則是這樣:
Action<FooBase> fooBaseAction = new Action<FooBase>((a)=>Console.WriteLine(a)); Action<Foo> fooAction = fooBaseAction;
而對于逆變,則跟協變相反,有下面的約束,否則也是編譯時報錯:
要想標識為逆變,應該是要在占位符T前標識in,只能用于只寫屬性、方法或者委托的輸入參數
當要進行類型轉換,占位符T要轉換的目標類型也必須是其子類,上述例子則是FooBase
轉為Foo
總結#
協變和逆變只對泛型委托和泛型接口有效,對普通的泛型類和泛型方法無效
協變和逆變的類型必須是引用類型,因為值類型不具備繼承性,因此類型轉換存在不兼容性
泛型接口和泛型委托可同時存在協變和逆變的類型參數,即占位符T
原文鏈接:https://www.cnblogs.com/ryzen/p/15771954.html
相關推薦
- 2022-08-26 docker搭建memcached的詳細步驟_docker
- 2022-04-30 Python代碼顯得Pythonic(區別于其他語言的寫法)_python
- 2022-09-16 JQuery實現電梯導航效果_jquery
- 2023-11-15 Python pip指定安裝鏡像源;pip安裝如何指定和更改鏡像源?
- 2022-05-24 Flutter滾動組件之SingleChildScrollView使用詳解_Android
- 2022-10-22 Redis?布隆過濾器命令的使用詳解_Redis
- 2023-01-18 Python函數的參數列表解析_python
- 2022-11-11 C語言qsort函數使用方法詳解_C 語言
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支