zoukankan      html  css  js  c++  java
  • Covariance and Contravariance in Generics

    Covariance and Contravariance in Generics

    奇怪的是微软有两篇文章来说明协变和逆变

    文章1 

    https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/covariance-contravariance/

    文章2   第二篇文章写得更清晰,是C#和VB双版本的

    Covariance and contravariance are terms that refer to the ability to use a more derived type (more specific) or a less derived type (less specific) than originally specified. Generic type parameters support covariance and contravariance to provide greater flexibility in assigning and using generic types. When you are referring to a type system, covariance, contravariance, and invariance have the following definitions. The examples assume a base class named Base and a derived class named Derived.

    • Covariance

      Enables you to use a more derived type than originally specified.

      You can assign an instance of IEnumerable<Derived> (IEnumerable(Of Derived) in Visual Basic) to a variable of type IEnumerable<Base>.

    • Contravariance

      Enables you to use a more generic (less derived) type than originally specified.

      You can assign an instance of Action<Base> (Action(Of Base) in Visual Basic) to a variable of type Action<Derived>.

    • Invariance

      Means that you can use only the type originally specified; so an invariant generic type parameter is neither covariant nor contravariant.

      You cannot assign an instance of List<Base> (List(Of Base) in Visual Basic) to a variable of type List<Derived> or vice versa.

    Covariant type parameters enable you to make assignments that look much like ordinary Polymorphism, as shown in the following code.

    IEnumerable<Derived> d = new List<Derived>();
    IEnumerable<Base> b = d;

    The List<T> class implements the IEnumerable<T> interface, so List<Derived> (List(Of Derived) in Visual Basic) implements IEnumerable<Derived>. The covariant type parameter does the rest.

    Contravariance, on the other hand, seems counterintuitive. The following example creates a delegate of type Action<Base> (Action(Of Base) in Visual Basic), and then assigns that delegate to a variable of type Action<Derived>.

    Action<Base> b = (target) => { Console.WriteLine(target.GetType().Name); };
    Action<Derived> d = b;
    d(new Derived());

    This seems backward, but it is type-safe code that compiles and runs. The lambda expression matches the delegate it is assigned to, so it defines a method that takes one parameter of type Base and that has no return value. The resulting delegate can be assigned to a variable of type Action<Derived> because the type parameter T of the Action<T> delegate is contravariant. The code is type-safe because T specifies a parameter type. When the delegate of type Action<Base> is invoked as if it were a delegate of type Action<Derived>, its argument must be of type Derived. This argument can always be passed safely to the underlying method, because the method's parameter is of type Base.

    In general, a covariant type parameter can be used as the return type of a delegate, and contravariant type parameters can be used as parameter types. For an interface, covariant type parameters can be used as the return types of the interface's methods, and contravariant type parameters can be used as the parameter types of the interface's methods.

    Covariance and contravariance are collectively referred to as variance. A generic type parameter that is not marked covariant or contravariant is referred to as invariant. A brief summary of facts about variance in the common language runtime:

    • In the .NET Framework 4, variant type parameters are restricted to generic interface and generic delegate types.

    • A generic interface or generic delegate type can have both covariant and contravariant type parameters.

    • Variance applies only to reference types; if you specify a value type for a variant type parameter, that type parameter is invariant for the resulting constructed type.

    • Variance does not apply to delegate combination. That is, given two delegates of types Action<Derived> and Action<Base> (Action(Of Derived) and Action(Of Base) in Visual Basic), you cannot combine the second delegate with the first although the result would be type safe. Variance allows the second delegate to be assigned to a variable of type Action<Derived>, but delegates can combine only if their types match exactly.

     public void Test()
            {
                //https://docs.microsoft.com/en-us/dotnet/standard/generics/covariance-and-contravariance
    
                //Covariance, Enables you to use a more derived type than originally specified.  用来做返回值
                //public interface IEnumerable<out T> : IEnumerable
                IEnumerable<Derived> derivedList = new List<Derived>();
                IEnumerable<Base> baseList = derivedList;
    
                //Contravariance, Enables you to use a more generic (less derived) type than originally specified.  用来做函数参数
                //public delegate void Action<in T>(T obj)
                Action<Base> actionBase = ActionMethod;
                Action<Derived> actionDerived = actionBase;
                actionDerived(new Derived());
    
                //Invariance
                //public class List<T>
                List<Base> baseList2 = new List<Base>();
                List<Derived> derivedList2 = new List<Derived>();
                //The following code is not allowed
                //baseList2 = derivedList2;
                //derivedList2 = baseList2;
            }
    
            public void ActionMethod(Base baseInstance)
            {
                Console.WriteLine(baseInstance.GetType().Name);
            }

    关于out的可以用来处理函数参数和返回值,Covariance enables you to use a more derived type than that specified by the generic parameter.

    private void ValidateEmailDomains(List<string> emailDomains) 可以被优化成下面的结构,参数类型使用IReadOnlyCollection<T>

    private void ValidateEmailDomains(IReadOnlyCollection<string> emailDomains)

    然后传参数的时候,参数的实例可以是List<string>,Queue<string>  参数变成了更具体的类型,可以说是把子类型的实例,传递给父类型的参数

  • 相关阅读:
    如何学习 websocket ?
    如何使用 C++ Inja html template 模板
    使用 QSqlTableModel 模型向数据库中插入数据时,为什么使用 rowCount 函数只能返回 256 最大值?
    windows 如何配置 Go 环境(Zip archive 方式)?
    qt 如何使用 lamda 表达式接收线程中发射的数据,并在里面更新 UI ?
    如何使用 VLD 检测程序中的内存泄漏?
    Qt 在相同的线程中可以在信号中传递未注册的元对象,在非相同线程中则不能传递未测试的对象,为什么呢?
    《LeetBook》leetcode题解(5):Longest Palindromic [M]——回文串判断
    《LeetBook》leetcode题解(4): Median of Two Sorted Arrays[H]——两个有序数组中值问题
    《Algorithms算法》笔记:元素排序(4)——凸包问题
  • 原文地址:https://www.cnblogs.com/chucklu/p/13755380.html
Copyright © 2011-2022 走看看