协变和逆变

来源:互联网 发布:治愈动漫电影知乎 编辑:程序博客网 时间:2024/05/01 15:18

协变和逆变都是术语,前者指能够使用比原始指定的派生类型的派生程度更大的类型,后者指能够使用比原始指定的派生类型的派生程度更小的类型。

using System;

using System.Collections.Generic;
using System.Text;


namespace ConsoleApplication1
{
    class Program
    {
        class Base
        {
            public static void PrintBases(IEnumerable<Base> bases)
            {
                foreach (Base b in bases)
                {
                    Console.WriteLine(b);
                }
            }
        }
        class Derived : Base
        { 
        
        }
        static void Main(string[] args)
        {
            List<Derived> dlist = new List<Derived>();


            Derived.PrintBases(dlist);
            IEnumerable<Base> bIEnum = dlist;
        }
    }

}

VS2008报错

错误 2 参数“1”: 无法从“System.Collections.Generic.List<ConsoleApplication1.Program.Derived>”转换为“System.Collections.Generic.IEnumerable<ConsoleApplication1.Program.Base>”

VS2010正常。

为什么呢 原来接口实现了协变

public interface IEnumerable<T> : IEnumerable

    public interface IEnumerable<outT> : IEnumerable

vs2010不一样了

从 .NET Framework 4 开始,某些泛型接口具有协变类型参数;例如:IEnumerable<T>IEnumerator<T>IQueryable<T> 和 IGrouping<TKey, TElement>。 由于这些接口的所有类型参数都是协变类型参数,因此这些类型参数只用于成员的返回类型。

下面的示例阐释了协变类型参数。 此示例定义了两个类型:Base 具有一个名为 PrintBases 的静态方法,该方法采用 IEnumerable<Base>(在 Visual Basic 中为 IEnumerable(Of Base))并输出元素。 Derived 继承自 Base 此示例创建一个空 List<Derived>(在 Visual Basic 中为 List(Of Derived)),并且说明可以将该类型传递给 PrintBases 且在不进行强制转换的情况下将该类型分配给类型 IEnumerable<Base> 的变量。 List<T> 实现 IEnumerable<T>,它具有一个协变类型参数。 协变类型参数是可使用 IEnumerable<Derived> 的实例而非 IEnumerable<Base> 的原因。

<span style="color:#2a2a2a;">namespace ConsoleApplication1{    class Program    {        abstract class Shape        {            public virtual double Area { get { return 0; } }        }        class Circle : Shape        {            private double r;            public Circle(double radius) { r = radius; }            public double Radius { get { return r; } }            public override double Area { get { return Math.PI * r * r; } }        }        class ShapeAreaComparer : System.Collections.Generic.</span><span style="color:#ff0000;">IComparer</span><span style="color:#2a2a2a;"><Shape>        {            int IComparer<Shape>.Compare(Shape a, Shape b)            {                if (a == null) return b == null ? 0 : -1;                return b == null ? 1 : a.Area.CompareTo(b.Area);            }        }        static void Main(string[] args)        {            SortedSet<Circle> circlesByArea =             new SortedSet<Circle>(new ShapeAreaComparer()) { new Circle(7.2), new Circle(100), null, new Circle(.01) };            foreach (Circle c in circlesByArea)            {                Console.WriteLine(c == null ? "null" : "Circle with area " + c.Area);            }            Console.Read();        }    }}</span>

从 .NET Framework 4 开始,某些泛型接口具有逆变类型参数;例如:IComparer<T>IComparable<T> 和 IEqualityComparer<T>。 由于这些接口只具有逆变类型参数,因此这些类型参数只用作接口成员中的参数类型。

下面的示例阐释了逆变类型参数。 该示例定义具有 Area 属性的抽象(在 Visual Basic 中为 MustInheritShape 类。 该示例还定义一个实现IComparer<Shape>(在 Visual Basic 中为 IComparer(Of Shape))的 ShapeAreaComparer 类。 IComparer<T>.Compare 方法的实现基于 Area 属性的值,所以 ShapeAreaComparer 可用于按区域对 Shape 对象排序。

Circle 类继承 Shape 并重写 Area。 该示例创建 Circle 对象的 SortedSet<T>,使用采用 IComparer<Circle>(在 Visual Basic 中为 IComparer(Of Circle))的构造函数。 但是,该对象不传递 IComparer<Circle>,而是传递一个用于实现 IComparer<Shape> 的 ShapeAreaComparer 对象。 当代码需要派生程度较大的类型的比较器 (Circle) 时,该示例可以传递派生程度较小的类型的比较器 (Shape),因为 IComparer<T> 泛型接口的类型参数是逆变参数。

向 SortedSet<Circle> 中添加新 Circle 对象时,每次将新元素与现有元素进行比较时,都会调用 ShapeAreaComparer 对象的 IComparer<Shape>.Compare方法(在 Visual Basic 中为 IComparer(Of Shape).Compare 方法)。 方法 (Shape) 的参数类型比被传递的类型 (Circle) 的派生程度小,所以调用是类型安全的 逆变使 ShapeAreaComparer 可以对派生自 Shape 的任意单个类型的集合以及混合类型的集合排序。



0 0
原创粉丝点击