C# 基礎系列--泛型(抗變和協變)


  泛型的抗變和協變是在.NET4.0中才增加, 這對之前的接口的一個不錯的擴展。抗變和協變是指針對參數和返回值的類型轉換。

  看了下評論,抗變和協變 在 msdn的翻譯是逆變和協變。我先是看C#高級編程第七版的中文版的,所以還是比較習慣抗變和協變。

  抗變和協變的在msdn的解釋

在 C# 和 Visual Basic 中,協變和逆變允許數組類型、委托類型和泛型類型參數進行隱式引用轉換。 協變保留分配兼容性,逆變與之相反。

  關鍵字的傳送門:out

通過協變,可以使用與泛型參數指定的派生類型相比,派生程度更大的類型。 這樣可以對委托類型和實現變體接口的類進行隱式轉換。 引用類型支持協變和逆變,但值類型不支持。

 in

通過逆變,可以使用與泛型參數指定的派生類型相比,派生程度更小的類型。 這樣可以對委托類型和實現變體接口的類進行隱式轉換。 引用類型支持泛型類型參數中的協變和逆變,但值類型不支持。

  

  在.NET中 參數類型是協變,返回值是抗變。

  不要廢話了,先定義兩個我們例子用的實體類---基類  RectangleBase  派生類--Rectangle 

 

 public class RectangleBase  
    {
        public int ID { get; set; }
    }

    public class Rectangle : RectangleBase
    {
        public string Name { get; set; }
    }

 

如果有個方法 public void Display(RectangleBase  r)  我們傳入一個 Rectangle 的實體,那么就是一個參數類型的協變

如果有個方法public RectangleBase   GetRectangle()  我們這里 RectangleBase b = GetRectangle() 那么這就是方法返回類型的抗變。

 在4.0之前,泛型接口是不擁有想上面類的便利性。很幸運,微軟在 4.0對泛型接口擴展這寫!

 

協變


 

  如果泛型接口中有關鍵字 out的,那個這個泛型接口就是協變。這個就定義了這個接口只能返回類型只能是T。

我們先定義一個接口

public interface IIndex<out T>
    {
        T this[int index] { get; }

        int Count { get; }
    }

  我們的實現類:

 

public class RectangleCollection : IIndex<Rectangle>
    {
        List<Rectangle> list = new List<Rectangle>();

        public Rectangle this[int index]
        {
            get 
            {
                if (index < 0 || index > Count)
                    throw new ArgumentOutOfRangeException("index");
                return list[index];
            }
        }

        public int Count
        {
            get { return list.Count; }
        } 
      public  void  Add(Rectangle value)
        {
                 list.Add(value);
        }
 }

  

  然后我們在控制台是這樣:

var list = new RectangleCollection();
            list.Add(new Rectangle { ID = 1, Name = "111" });
            list.Add(new Rectangle { ID = 2, Name = "222" });
            list.Add(new Rectangle { ID = 3, Name = "33" });

            IIndex<RectangleBase> Bas = list;

            for (int i = 0; i < Bas.Count; i++)
            {
                Console.WriteLine(Bas[i].ID);   
            }

            Console.Read();
  

  

 

  協變很簡單吧。。。如果你吧關鍵字out 去掉后,編譯器很快就會個告訴你  IIndex<RectangleBase> Bas = list; 錯誤。因為你沒有告訴他 這個T是可以變的

 

抗變


 

   如果泛型接口有關鍵字in ,那么表示這個泛型接口是可以抗變的。這樣,接口也只能把泛型類型T用作方法的輸入。

 

 定義泛型抗變的 接口

public interface IDisplay(in T)
{
        void Show(T item);
}

  抗變類:

public class RectangleDisplay:  IDisplay<RectangleBase>
{
       public void Show(RectangBase item)
      {
            Console.WrileLine(item.ID);
    }

}

  

 這篇寫的不是很好,因為我自己也不是吃的很透,今天在朋友的稍稍點撥下,算是有點 理解這個了。第一次接觸這個東西到現在又一年多了,今天才理解點,慚愧慚愧。

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM