C#语法手册

泛型中的逆变与协变


C# 中 逆变与协变的作用与基本语法。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CSGDemo
{

    //https://docs.microsoft.com/zh-cn/dotnet/standard/generics/covariance-and-contravariance


    //#csg015-01
    //术语:协变与逆变

    //协变/Covariance:
    //使你能够使用比原始指定的类型派生程度更大的类型。比如,你可以将 IEnumerable<Derived> 的实例分配给 IEnumerable<Base> 类型的变量。
    //协变的类型参数使用 out 关键字声明

    //逆变/Contravariance:
    //使你能够使用比原始指定的类型更泛型(派生程度更小)的类型。比如,你可以将 Action<Base> 的实例分配给 Action<Derived> 类型的变量。
    //逆变的类型参数使用 int 关键字声明

    //只有接口和委托中的泛型类型参数可以使用in 和 out


    public class DemoCode
    {

        //#csg015-02
        //一个泛型接口的协变例子,IList<T> 中的 T 是协变类型参数
        public void CovarianceInterface()
        {
            IEnumerable<Derived> d = new List<Derived>();
            //你可以将【派生类的泛型类型】的实例赋值给【基类的泛型类型】的变量
            IEnumerable<Base> b = d;

        }

        //#csg015-03
        //一个泛型委托的协变例子,Func<TResult> 中的 TResult 是协变类型参数
        public void CovarianceDelegate()
        {
            Func<Derived> fd = () => new Derived();
            //你可以将【派生类的泛型类型】的实例赋值给【基类的泛型类型】的变量
            Func<Base> fb = fd;

        }


        //#csg015-04
        //一个泛型接口的逆变例子,IComparer<T> 中的 T 是逆变类型参数
        public void ContravarianceInterface()
        {
            IComparable<Base> b = new DemoComparable<Base>();
            //你可以将【基类泛型类型】的实例赋值给【派生类泛型类型】的变量
            IComparable<Derived> d = b;
        }


        //#csg015-05
        //一个泛型委托的逆变例子, Action<T> 中的 T 是逆变类型参数
        public void ContravarianceDelegate()
        {
            Action<Base> b = (x) => Console.WriteLine(x);
            //你可以将【基类泛型类型】的实例赋值给【派生类泛型类型】的变量
            Action<Base> d = b;
        }


        //#csg015-06
        //除了逆变和协变,泛型类型参数也可以是不可变(Invariance)的,比如 List<T> 
        //表示变量只能赋值为使用最初的类型的实例。 固定泛型类型参数既不是协变,也不是逆变。
        public void Invariance()
        {
            List<Base> b = new List<Base>();
            //List<Derived> d = b; //该行代码报 CS0029 错误。
        }


        //#csg015-07
        //还有一种情况,Variant
        //一个泛型类型可以同时包含协变和逆变的类型参数, Func<T,TResult> 中的 T 是逆变类型参数,而 TResult 是协变类型参数
        public void Variant()
        {
            //逆变和协变在同一个类中的混合使用
            Func<Base, Derived> func = (b) => new Derived();

            Func<Derived, Base> func2 = func;
        }


    }


    public class Base { }


    public class Derived : Base { }

    public class DemoComparable<T> : IComparable<T>
    {
        public int CompareTo(T? other)
        {
            throw new NotImplementedException();
        }
    }

}