共變 逆變 傻傻分不清
前言
泛型的 T 是不變性
你給的類別 無論是 子類 或 父類 都不能取代
基本介紹
C# 的方法
傳回值 支援 共變性Covariance
傳入參數 支援 逆變性Contravariance
public interface ILevel
{
int LevelId { get; set; }
}
public class Level1: ILevel
{
public int LevelId { get; set; }
public string Name { get; set; }
public int test1 { get; set; }
}
public class Level2: Level1
{
public int test2 { get; set; }
}
public class Level3 : Level2
{
public int test3 { get; set; }
}
我寫了 三個類別 繼續繼承下去
public A01B_Covariance()
{
//共變性
Level1 l1 = NewLevel2();
//Level3 l3 = NewLevel2(); 編譯失敗
//逆變性
//GetLevel2(new Level1()); 編譯失敗
GetLevel2(new Level3());
}
private Level2 NewLevel2()
{
return new Level2();
}
private void GetLevel2(Level2 a)
{
}
共變性 就是 基底類別可取代衍生類別
Level1 l1 = NewLevel2();
因為level2 有 level1的資料 所以 能 當作level1 回傳使用
Level3 l3 = NewLevel2(); 編譯失敗
因為level2 沒有 level3的資料 所以 不能 當作level3 回傳使用
逆變性 就是 衍生類別可取代基底類別
GetLevel2(new Level1()); 編譯失敗
因為level1 沒有 level2的資料 所以 不能 當作level2 傳入參數
GetLevel2(new Level3());
因為level3 有 level2的資料 所以 能 當作level2 傳入參數
泛型的 共變性 逆變性
泛型 T 就是不變性 子類 或 父類 都不能取代
加上out T 就是 共變 Covariance , 此時的T 就只能是回傳型別
加上in T 就是 逆變 Contravariance , 此時的T 就只能是傳入型別
程式碼如下
public interface ILevelGet<out T>
{
T Get(int id);
}
public interface ILevelAdd<in T>
{
void Add(T level);
}
public class LevelPool<T> : ILevelGet<T>, ILevelAdd<T>
where T : class, ILevel, new()
{
private List<T> Pool { get; }
public LevelPool()
{
this.Pool = new List<T>();
}
public void Add(T level)
{
this.Pool.Add(level);
}
public T Get(int id)
{
var t = this.Pool.SingleOrDefault(x => x.LevelId == id) ?? new T();
return t;
}
}
再來看 使用時的程式碼 先看一下 共變性
public void testCovariant()
{
this.Level2Pools = new LevelPool<Level2>();
//out 共變性 Covariant
//基底取代衍生
GetLevel1(this.Level2Pools);
GetLevel2(this.Level2Pools);
//GetLevel3(this.Level2Pools);編譯失敗
}
//interface ILevelGet<out T>
private void GetLevel1(ILevelGet<Level1> repository)
{
var level1 = repository.Get(301);
Console.WriteLine(level1.LevelId);
}
private void GetLevel2(ILevelGet<Level2> repository)
{
var level2 = repository.Get(301);
Console.WriteLine(level2.LevelId);
}
private void GetLevel3(ILevelGet<Level3> repository)
{
var level3 = repository.Get(301);
Console.WriteLine(level3.LevelId);
}
接下去是 逆變性
public void testContravariant()
{
this.Level2Pools = new LevelPool<Level2>();
//in 逆變性 Contravariant
//衍生取代基底
//AddLevel1(this.Level2Pools);編譯失敗
AddLevel2(this.Level2Pools);
AddLevel3(this.Level2Pools);
}
//interface ILevelAdd<in T>
private void AddLevel1(ILevelAdd<Level1> repository)
{
repository.Add(new Level1 { LevelId = 101, Name = "Level1" });
}
private void AddLevel2(ILevelAdd<Level2> repository)
{
repository.Add(new Level2 { LevelId = 201, Name = "Level2" });
}
private void AddLevel3(ILevelAdd<Level3> repository)
{
repository.Add(new Level3 { LevelId = 301, Name = "Level3" });
}
結語
理解了共變 逆變 在泛型設計上 又更多彈性可以運用了
參考連結
C# 4.0:Covariance 與 Contravariance 觀念入門
如果內容有誤請多鞭策謝謝