• C#协变与逆变


    http://zh.wikipedia.org/wiki/%E5%8D%8F%E5%8F%98%E4%B8%8E%E9%80%86%E5%8F%98

    协变与逆变程序设计语言中的类型系统的一对概念。类型系统支持子类型。例如,如果CatAnimal的子类型,那么Cat类型的表达式可用于任何出现Animal类型表达式的地方。 变型(variance)是指,如何根据其组成类型来确定更复杂的类型(如Cat列表对Animal列表,返回Cat的函数对返回Animal的函数,...,等等。)之间的子类型关系。依赖于类型构造器的变型性质,复杂类型的子类型性质可分为保持、逆转、与忽略。例如,在C Sharp中:

    • IEnumerable<Cat>IEnumerable<Animal>的子类型,因为IEnumerable<T>类型构造器是协变(covariant)。也即,界面的最初的类型参数的子类型关系保持住了。
    • Action<Cat>Action<Animal>的超类型,因为Action<T>类型构造器是逆变(contravariant),其中Action<T>表示一个头等函数,需要一个类型参数为Tsub-T。注意对于T的子类型关系,在Action复杂类型封装下是逆转的。
    • IList<Cat>IList<Animal>彼此之间没有子类型关系。因为IList<T>类型构造器是不变(invariant).

    程序语言的设计者需要考虑针对数组、继承、泛型数据类型等的类型规则的“变型”。通过使得类型构造器是协变、逆变而不是“不变”,使得更多的程序可作为良好类型被接受。另一方面,程序员经常觉得逆变是不直观的;如果为避免运行时刻错误而精确跟踪变型将导致复杂的类型规则。为了保持类型系统简单,允许有用的编程,程序设计语言可把类型构造器处理为“不变”,即使它其实作为“变型”也是类型安全的;或者把类型构造器处理为协变,即使这会导致违背类型安全。

    using System;
    
    namespace TestCovarianceContravariance
    {
        public class Shape
        {
            public double Width { get; set; }
            public double Height { get; set; }
    
            public override string ToString()
            {
                return String.Format("Width: {0}, Height: {1}", Width, Height);
            }
        }
    
        public class Rectangle : Shape
        {
        }
    
        //如果泛型类型用out关键字标注,泛型接口就是协变的。这也意味着返回类型只能是T。
        //接口IIndex与类型T是协变的,并从一个只读索引器中返回这个类型
        public interface IIndex<out T>
        {
            T this[int index] { get; }
            int Count { get; }
        }
    
        public class RectangleCollection : IIndex<Rectangle>
        {
            private Rectangle[] data = new Rectangle[3]
                                           {
                                               new Rectangle {Height = 2, Width = 5},
                                               new Rectangle {Height = 3, Width = 7},
                                               new Rectangle {Height = 4.5, Width = 2.9}
                                           };
    
            public static RectangleCollection GetRectangles()
            {
                return new RectangleCollection();
            }
    
            public Rectangle this[int index]
            {
                get
                {
                    if (index < 0 || index > data.Length)
                        throw new ArgumentOutOfRangeException("index");
                    return data[index];
                }
            }
    
            public int Count
            {
                get { return data.Length; }
            }
        }
    
        internal class Program
        {
            private static void Main(string[] args)
            {
                IIndex<Rectangle> rectangles =
                    RectangleCollection.GetRectangles();
                IIndex<Shape> shapes = rectangles;
    
                for (int i = 0; i < shapes.Count; i++)
                {
                    Console.WriteLine(shapes[i]);
                }
    
                Console.WriteLine("##########");
                
                IDisplay<Shape> shapeDisplay = new ShapeDisplay();
                IDisplay<Rectangle> rectangleDisplay = shapeDisplay;
                rectangleDisplay.Show(rectangles[0]);
    
                Console.WriteLine("########## .net 框架中的示例 ###########");
    
                /*
                 public interface IEnumerable<out T> : IEnumerable      //协变
                 
                 public delegate void Action<in T>(      //逆变
    	            T obj
                )
                 */
    
                Action<Shape> b = (target) => { Console.WriteLine(target.GetType().Name); };
                Action<Rectangle> d = b; //逆变:Shape是Rectangle的超类型,而Action<Rectangle>却是Action<Shape>的超类型
                d(new Rectangle());
                b(new Shape());
    
                Console.ReadKey();
            }
        }
    
    
        public interface IDisplay<in T>     //该类型参数是逆变。即可以使用指定的类型或派生程度更低的类型。
        {
            void Show(T item);
        }
    
        public class ShapeDisplay : IDisplay<Shape>
        {
            public void Show(Shape s)
            {
                Console.WriteLine("{0} Width: {1}, Height: {2}", s.GetType().Name,
                                  s.Width, s.Height);
            }
        }
    
    }
    

      

  • 相关阅读:
    修复 XE8 for Android 方向传感器 headingX,Y,Z 不会动的问题
    修复 XE8 for Android 分享图片到 Gmail 权限不足的问题
    Firemonkey 载入 Style 皮肤 (*.fsf 二进制文件) 速度测试
    调整 FMX Android 文字显示「锯齿」效果
    [原创工具] ListView 调色盘 (Free)
    有关Linux的可执行程序
    Android 下配置一个 /dev/fb0 节点出来
    Android下运行Linux可执行程序
    数据库的范式
    rk3128 适配 USB 摄像头
  • 原文地址:https://www.cnblogs.com/wucg/p/3569170.html
Copyright © 2020-2023  润新知