.NET泛型03,泛型类型的转换,协变和逆变

协变(Convariant)和逆变(Contravariant)的出现,使数组、委托、泛型类型的隐式转换变得可能。 子类转换成基类,称之为协变;基类转换成子类,称之为逆变。.NET4.0以来,支持了泛型接口的协变和逆变。

泛型协变

如果子类泛型隐式转换成基类泛型,使用泛型协变。

有这样的2个基类和派生类。

public class Animal
    {
        public virtual void Write()
        {
            Console.WriteLine("我是基类");
        }
    }

    public class Dog : Animal
    {
        public override void Write()
        {
            Console.WriteLine("我是小小狗");
        }
    }

为了让派生类Dog隐式转换成基类Animal,先定义支持协变的泛型接口。

//支持协变的接口
    public interface IFactory<out T>
    {
        T Create();
    }

再实现这个接口。

public class Factory<T> : IFactory<T>
    {

        public T Create()
        {
            return (T)Activator.CreateInstance<T>();
        }
    }

客户端调用。

class Program
    {
        static void Main(string[] args)
        {
            IFactory<Dog> dogFactory = new Factory<Dog>();
            IFactory<Animal> animalFactory = dogFactory; //协变
            Animal animal = animalFactory.Create();
            animal.Write();
            Console.ReadKey();
        }
    }

运行输出:我是小小狗

以上,我们可以看出:
● 协变后,父类的方法完全由子类替代,父类原先的方法不复存在
● 泛型接口中的out关键字必不可少

泛型逆变

关于通知的一个接口。

public interface INotification
    {
        string Message { get; }
    }

关于通知接口的抽象实现。

public abstract class Notification : INotification
    {
        public abstract string Message { get; }
    }

关于通知抽象类的具体实现。

public class MailNotification : Notification
    {
        public override string Message
        {
            get { return "你有邮件了~~"; }
        }
    }

接下来,需要把通知的信息发布出去,需要一个发布通知的接口INotifier,该接口依赖INotification,大致INotifier<INotification>,而最终显示通知,我们希望INotifier<MailNotification>,INotifier<INotification>转换成INotifier<MailNotification>,这是逆变,需要关键字in。

public interface INotifier<in TNotification> where TNotification : INotification
    {
        void Notify(TNotification notification);
    }

实现INotifier。

public class Notifier<TNotification> : INotifier<TNotification> where TNotification : INotification
    {

        public void Notify(TNotification notification)
        {
            Console.WriteLine(notification.Message);
        }
    }

客户端调用。

class Program
    {
        static void Main(string[] args)
        {
            INotifier<INotification> notifier = new Notifier<INotification>();
            INotifier<MailNotification> mailNotifier = notifier;//逆变
            mailNotifier.Notify(new MailNotification());
            Console.ReadKey();
        }
    }

运行输出:你有邮件了~~

以上,我们可以看出:
● INotifier的方法Notify()的参数类型是INotification,逆变后把INotification类型参数隐式转换成了实现类MailNotificaiton。
● 泛型接口中的in关键字必不可少

参考资料:
《你必须知道的.NET(第2版)》,作者王涛。

".NET泛型"系列包括:

.NET泛型01,为什么需要泛型,泛型基本语法

.NET泛型02,泛型的使用

.NET泛型03,泛型类型的转换,协变和逆变

.NET泛型03,泛型类型的转换,协变和逆变

时间: 2024-11-05 18:33:48

.NET泛型03,泛型类型的转换,协变和逆变的相关文章

泛型在接口中的协变、逆变练习

前言: 泛型在接口和委托中里氏替换原则语法编译不过,如何解决? 先来个大佬的链接: http://www.cnblogs.com/lemontea/archive/2013/02/17/2915065.html 关于协变:(父类声明,子类实例化) 关于逆变的练习:(子类声明,父类实例化) 原文地址:https://www.cnblogs.com/Innocent-of-Dabber/p/9310835.html

泛型--协变与逆变(转)

对于泛型的知识,一直比较模糊,现在有机会整理一下,突发发现C#还有很多你不知道的东东,继续.NET FrameWork中泛型的协变与逆变: 1. 可变性的类型:协变性和逆变性 可变性是以一种类型安全的方式,将一个对象当做另一个对象来使用.如果不能将一个类型替换为另一个类型,那么这个类型就称之为:不变量.协变和逆变是两个相互对立的概念: 如果某个返回的类型可以由其派生类型替换,那么这个类型就是支持协变的 如果某个参数类型可以由其基类替换,那么这个类型就是支持逆变的. 2. C# 4.0对泛型可变性

泛型的协变和逆变

转载C# 泛型的协变和逆变 1. 可变性的类型:协变性和逆变性 可变性是以一种类型安全的方式,将一个对象当做另一个对象来使用.如果不能将一个类型替换为另一个类型,那么这个类型就称之为:不变量.协变和逆变是两个相互对立的概念: 如果某个返回的类型可以由其派生类型替换,那么这个类型就是支持协变的 如果某个参数类型可以由其基类替换,那么这个类型就是支持逆变的. 2. C# 4.0对泛型可变性的支持 在C# 4.0之前,所有的泛型类型都是不变量——即不支持将一个泛型类型替换为另一个泛型类型,即使它们之间

Java泛型中的协变和逆变

Java泛型中的协变和逆变 一般我们看Java泛型好像是不支持协变或逆变的,比如前面提到的List<Object>和List<String>之间是不可变的.但当我们在Java泛型中引入通配符这个概念的时候,Java 其实是支持协变和逆变的. 看下面几行代码: // 不可变 List<Fruit>fruits =newArrayList<Apple>();// 编译不通过 // 协变 List<?extendsFruit>wildcardFruit

[C#]浅谈协变与逆变

看过几篇说协变与逆变的博客,虽然都是正确无误的,但是感觉都没有说得清晰明了,没有切中要害.那么我也试着从我的理解角度来谈一谈协变与逆变吧. 什么是协变与逆变 MSDN的解释:https://msdn.microsoft.com/zh-cn/library/dd799517.aspx 协变和逆变都是术语,前者指能够使用比原始指定的派生类型的派生程度更小(不太具体的)的类型,后者指能够使用比原始指定的派生类型的派生程度更大(更具体的)的类型.泛型类型参数支持协变和逆变,可在分配和使用泛型类型方面提供

.NET可变性解析(协变和逆变)

[一]何为可变性 可变性是.NET4.0中的一个新特性,可变性可分为 : 协变性.逆变性.不可变性. 那么在.NET4.0之前是否有可变性? 答案是肯定的,我们可以通过下面的几个实例来简单的了解一下.NET4.0之前的协变和逆变. 实例 1 : 方法参数的协变 static void Main(string[] args) { GetProject(new Course()); // Course 继承自 Project 此处进行了协变 } static void GetProject(Proj

协变与逆变

迁移 https://huangshubi.github.io/2020/02/14/%E5%8D%8F%E5%8F%98%E4%B8%8E%E9%80%86%E5%8F%98/ 记录 官方文档的协变与逆变学习过程. 使用举例 协变与逆变能够实现数组类型.委托类型和泛型接口参数的隐式引用转换. 1.委托类型 namespace ConsoleApp4 { class Program { static void Main(string[] args) { Func<Bird> birdFunc

厘清泛型参数的协变与逆变

协变与逆变(CoVariant and ContraVariant),很多人是糊涂的,我也一直糊涂.其实,对协变与逆变概念糊涂,甚至完全不知道,对一般程序员也没有很大影响.不过,如果你想提高水平,想大概看懂.Net Framework类库中那些泛型接口与泛型类,想大概弄清楚Linq,这个概念还是需要搞清楚. 话又说回来,想弄清楚,其实还是挺费劲的. 如果你还糊涂着这两个概念,相信我,认真看完下面的文字,你会对泛型参数的协变与逆变有一个清晰的理解. 想透彻掌握协变.逆变的概念,首先需要对接口.委托

泛型中协变和逆变

写在前面 今天讲的内容有点多,但是差不多都能听懂,稍微有点模糊的就是协变和逆变的概念,下面是我结合在网上看的资料整合而成的. 正文 msdn上的原话: 协变:是指能够使用比原始指定的派生类型的派生程度更小(不太确定)的类型 逆变:是指能够使用比原始类型的派生类型的派生程度更大(更具体)的类型 在方便理解的概念是: 协变:子类向父类转化,用于返回类型用out关键字 逆变:父类向子类转化的过程,用于方法参数类型用in关键字 协变的例子: 1 public class Person { } 2 3 p