变体浅析(C#)

本文深入介绍了.NET 4.0 中的变体特性,包括协变和抗变的概念,通过实例展示了如何使用变体实现泛型的灵活应用。文章还详细介绍了变体在类继承和泛型接口之间的使用场景,以及如何通过变体实现类型间的转换和泛型集合的灵活操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

今天简单说说.net 4.0里面的新特性:变体

我们知道可以用父类对象指向子类引用,我写两个简单的类。

  1. public abstract class Shape  
  2. {  
  3.     public float Width { getset; }  
  4.     public float Height { getset; }  
  5.   
  6.     public abstract float GetPerimeter();  
  7.     public abstract float GetArea();  
  8. }  
    public abstract class Shape
    {
        public float Width { get; set; }
        public float Height { get; set; }

        public abstract float GetPerimeter();
        public abstract float GetArea();
    }
  1. public class Rectangle : Shape  
  2. {  
  3.     public Rectangle(float width, float height)  
  4.     {  
  5.         this.Width = width;  
  6.         this.Height = height;  
  7.     }  
  8.   
  9.     public override float GetPerimeter()  
  10.     {  
  11.         return 2 * (Width + Height);  
  12.     }  
  13.   
  14.     public override float GetArea()  
  15.     {  
  16.         return Width * Height;  
  17.     }  
  18. }  
    public class Rectangle : Shape
    {
        public Rectangle(float width, float height)
        {
            this.Width = width;
            this.Height = height;
        }

        public override float GetPerimeter()
        {
            return 2 * (Width + Height);
        }

        public override float GetArea()
        {
            return Width * Height;
        }
    }


 

那么我们可以用如下代码实例化Rectangle类:

  1. Shape sh = new Rectangle(3, 4);  
Shape sh = new Rectangle(3, 4);


当然这也是实现多态性的前提条件,但是这个却不适用于泛型,比如无法用如下代码实例化List<Rectangle>类:

  1. List<Shape> shs = new List<Rectangle>();  
List<Shape> shs = new List<Rectangle>();

 

不过有了变体之后我们便可以实现这种代码了,变体分为两种:协变 和 抗变

 

1、协变

协变用out关键字声明,比如GenericClass<out T>,协变中,T类型只能用作输出参数,并且允许在泛型中用父类指向子类引用。

2、抗变

抗变用in关键字声明,比如GenericClass<in T>,抗变中,T类型只能用作输入参数,不过抗变有点奇怪,抗变允许在泛型中用子类指向父类引用。

 

我下面用一个例子来简单描述一下变体。

先扩充一个类:

  1. public class RichRectangle : Rectangle  
  2. {  
  3.     public int BorderWidth { getset; }  
  4.     public Color BorderColor { getset; }  
  5.     public Color BackColor { getset; }  
  6.   
  7.     public RichRectangle(float width, float height)  
  8.         : this(width, height, 1, Color.Black, Color.White)  
  9.     { }  
  10.     public RichRectangle(float width, float height, int bdrWidth, Color bdrColor, Color bgColor)  
  11.         : base(width, height)  
  12.     {  
  13.         this.BorderWidth = bdrWidth;  
  14.         this.BorderColor = bdrColor;  
  15.         this.BackColor = bgColor;  
  16.     }  
  17. }  
    public class RichRectangle : Rectangle
    {
        public int BorderWidth { get; set; }
        public Color BorderColor { get; set; }
        public Color BackColor { get; set; }

        public RichRectangle(float width, float height)
            : this(width, height, 1, Color.Black, Color.White)
        { }
        public RichRectangle(float width, float height, int bdrWidth, Color bdrColor, Color bgColor)
            : base(width, height)
        {
            this.BorderWidth = bdrWidth;
            this.BorderColor = bdrColor;
            this.BackColor = bgColor;
        }
    }


定义IIndex和IAppend接口和一个RectangleCollection类

  1. public interface IIndex<out T>  
  2. {  
  3.     T this[int index] { get; }  
  4.     int Count { get; }  
  5. }  
    public interface IIndex<out T>
    {
        T this[int index] { get; }
        int Count { get; }
    }
  1. public interface IAppend<in T>  
  2. {  
  3.     void Add(T item);  
  4.     void Insert(int index, T item);  
  5. }  
    public interface IAppend<in T>
    {
        void Add(T item);
        void Insert(int index, T item);
    }
  1. public class RectangleCollection : IIndex<Rectangle>, IAppend<Rectangle>, IEnumerable<Rectangle>  
  2. {  
  3.     private List<Rectangle> rects;  
  4.   
  5.     public RectangleCollection()  
  6.     {  
  7.         this.rects = new List<Rectangle>();  
  8.     }  
  9.   
  10.     public Rectangle this[int index]  
  11.     {  
  12.         get { return rects[index]; }  
  13.     }  
  14.   
  15.     public int Count  
  16.     {  
  17.         get { return rects.Count; }  
  18.     }  
  19.   
  20.     public void Add(Rectangle item)  
  21.     {  
  22.         this.rects.Add(item);  
  23.     }  
  24.   
  25.     public void Insert(int index, Rectangle item)  
  26.     {  
  27.         this.rects.Insert(index, item);  
  28.     }  
  29.   
  30.     public IEnumerator<Rectangle> GetEnumerator()  
  31.     {  
  32.         return this.rects.GetEnumerator();  
  33.     }  
  34.   
  35.     IEnumerator IEnumerable.GetEnumerator()  
  36.     {  
  37.         return GetEnumerator();  
  38.     }  
  39. }  
    public class RectangleCollection : IIndex<Rectangle>, IAppend<Rectangle>, IEnumerable<Rectangle>
    {
        private List<Rectangle> rects;

        public RectangleCollection()
        {
            this.rects = new List<Rectangle>();
        }

        public Rectangle this[int index]
        {
            get { return rects[index]; }
        }

        public int Count
        {
            get { return rects.Count; }
        }

        public void Add(Rectangle item)
        {
            this.rects.Add(item);
        }

        public void Insert(int index, Rectangle item)
        {
            this.rects.Insert(index, item);
        }

        public IEnumerator<Rectangle> GetEnumerator()
        {
            return this.rects.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    }


IIndex<T>是协变的泛型接口、IAppend<T>是抗变的泛型接口,写一个测试类:

  1. class Program  
  2. {  
  3.     static void Main(string[] args)  
  4.     {  
  5.         var rects = new RectangleCollection();  
  6.         var rect1 = new Rectangle(3, 4);  
  7.         var rect2 = new Rectangle(7, 4);  
  8.         var rect3 = new Rectangle(4, 8);  
  9.         rects.Add(rect1);  
  10.         rects.Add(rect2);  
  11.         rects.Add(rect3);  
  12.         IIndex<Shape> shapes = rects;//这里由于IIndex是协变,因此可以指向实现了IIndex<Rectangle>的RectangleCollection类   
  13.         IAppend<RichRectangle> richRects = rects;//同理,这里是逆变,可以指向父类   
  14.         richRects.Add(new RichRectangle(5, 5));  
  15.         foreach (var rect in rects)  
  16.         {  
  17.             Console.WriteLine(rect.GetPerimeter() + ", " + rect.GetArea());  
  18.         }  
  19.   
  20.         Console.ReadKey();  
  21.     }  
  22. }  
    class Program
    {
        static void Main(string[] args)
        {
            var rects = new RectangleCollection();
            var rect1 = new Rectangle(3, 4);
            var rect2 = new Rectangle(7, 4);
            var rect3 = new Rectangle(4, 8);
            rects.Add(rect1);
            rects.Add(rect2);
            rects.Add(rect3);
            IIndex<Shape> shapes = rects;//这里由于IIndex是协变,因此可以指向实现了IIndex<Rectangle>的RectangleCollection类
            IAppend<RichRectangle> richRects = rects;//同理,这里是逆变,可以指向父类
            richRects.Add(new RichRectangle(5, 5));
            foreach (var rect in rects)
            {
                Console.WriteLine(rect.GetPerimeter() + ", " + rect.GetArea());
            }

            Console.ReadKey();
        }
    }



 转自:http://blog.youkuaiyun.com/wanxindavid/article/details/8525378

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值