·天新网首页·加入收藏·设为首页·网站导航
数码笔记本手机摄像机相机MP3MP4GPS
硬件台式机网络服务器主板CPU硬盘显卡
办公投影打印传真
家电电视影院空调
游戏网游单机动漫
汽车新车购车试驾
下载驱动源码
学院开发设计
考试公务员高考考研
业界互联网通信探索
您现在的位置:天新网 > 软件开发 > .Net开发 > C#
[C# 基础知识系列]专题八:深入理解泛型可变性
http://www.21tx.com 2012年11月29日

1 2 下一页

引言:

  在C# 2.0中泛型并不支持可变性的(可变性指的就是协变性和逆变性),我们知道在面向对象的继承中就具有可变性,当方法声明返回类型为Stream,我们可以在实现中返回一个FileStream的类型,此时就存在一个隐式的转化——从FileStream类型(子类引用)——>Stream类型(父类引用),并且引用类型的数组也存在这种从子类引用——>父类引用的转化,例如string[] 可以转化为object[](即这样的代码是可以通过编译的:string[] strs =new string[3]; object[] objs =strs;),此时我们肯定会想是否泛型中的泛型参数也可以支持这样的转化呢?然而在C# 2.0中是不支持的,但是就是因为有这样的需求,所以微软也考虑到这个问题的, 所以在C# 4.0中就引入了泛型的协变和逆变性。下面就具体来介绍下C# 4.0 中对协变和逆变的具体内容有哪些的。

一、协变性

  协变性指的是——泛型类型参数可以从一个派生类隐式转化为基类(大家可以这样记忆的,协变性即和谐的变化,生活中我们一般会说子女长的像他们的父母,这样听起来会感觉比较和谐点,这样就很容易记住协变了),在C#4.0中引入out关键字来标记泛型参数支持协变性。为了更好的说明泛型的协变性,下面就以.net类库的中public interface IEnumerable<out T>这个接口来演示一个例子来帮助大家理解泛型协变:

List<object> listobject = new List<object>();   
            List<string> liststrs = new List<string>();   
            // AddRange方法接收的参数类型为IEnumerable<T> collection   
            // 下面的代码是传入的是List<string>类型的参数。   
            // 在MSDN中可以看出这个接口的定义为——IEnumerable<int T>。   
            // 所以 IEnumerable<T>泛型类型参数T支持协变性,所以可以   
            // 将List<string>转化为IEnumerable<string>(这个是继承的协变性支持的)   
            // 又因为这个IEnumerable<in T>接口委托支持协变性,所以可以把IEnumerable<string>转化为——>IEnumerable<object>类型。   
            // 所以编译器验证的时候就不会出现类型不能转化的错误了。   
            listobject.AddRange(liststrs);  //成功   
       
            liststrs.AddRange(listobject); // 出错

代码中如果使用 这代码时 liststrs.AddRange(listobject); 就会出现编译时错误(无法从List<object>转换为IEnumerable<string>,因为List<object>可以因为继承的协变性转化为IEnumerable<object>,但是因为IEnumerable<out T>不支持逆变,即从object到string的转化,所以此时就会产生下面图中的错误了。), 错误提示截图如下:

[C# 基础知识系列]专题八:深入理解泛型可变性

二、逆变性

  逆变性指的是——泛型类型参数可以从一个基类隐式转化为派生类(可以从生活中的例子来帮助大家记忆逆变的——如果说父母长的像他们的子女的话肯定觉得别扭,在高中语文中经常会找这样的语病的),在C# 4.0中引入in关键字来标记泛型参数支持逆变性.为了更好的说明泛型的逆变性,下面就以.Net类库的中接口public interface IComparer<in T>来演示一个例子来帮助大家理解泛型逆变:

class Program   
    {   
        static void Main(string[] args)   
        {   
            List<object> listobject = new List<object>();   
            List<string> liststrs = new List<string>();   
            // AddRange方法接收的参数类型为IEnumerable<T> collection   
            // 下面的代码是传入的是List<string>类型的参数。   
            // 在MSDN中可以看出这个接口的定义为——IEnumerable<int T>。   
            // 所以 IEnumerable<T>泛型类型参数T支持协变性,所以可以   
            // 将List<string>转化为IEnumerable<string>(这个是继承的协变性支持的)   
            // 又因为这个IEnumerable<in T>接口委托支持协变性,所以可以把IEnumerable<string>转化为——>IEnumerable<object>类型。   
            // 所以编译器验证的时候就不会出现类型不能转化的错误了。   
            listobject.AddRange(liststrs);  //成功   
       
            ////liststrs.AddRange(listobject); // 出错   
       
            IComparer<object> objComparer = new TestComparer();   
            IComparer<string> objComparer2 = new TestComparer();   
       
            // List<string>类型的 liststrs变量的sort方法接收的是IComparer<string>类型的参数   
            // 然而下面代码传入的是 IComparer<object>这个类型的参数,要编译成功的话,必须能够转化为IComparer<string>这个类型   
            // 正是因为IComparer<in T>泛型接口支持逆变,所以支持object转化为string类型   
            // 所以下面的这行代码可以编译通过,在.Net 4.0之前的版本肯定会编译错误,   
            // 大家可以把项目的目标框架改为.Net Framework 3.5或者更加低级的版本   
            // 这样下面这行代码就会出现编译错误,因为泛型的协变和逆变是C# 4.0 中新增加的特性,而.Net 4.0对应于C# 4.0。   
            liststrs.Sort(objComparer);  // 正确   
       
            // 出错   
            ////listobject.Sort(objComparer2);   
        }       
    }   
       
    public class TestComparer : IComparer<object>   
    {   
        public int Compare(object obj1,object obj2)   
        {   
            return obj1.ToString().CompareTo(obj2.ToString());   
        }   
    }

上面代码中如果使用 listobject.Sort(objComparer2);时,就会出现编译错误,错误原因看过上面协变中错误原因的解释应该都可以明白的,下面是错误的截图:

[C# 基础知识系列]专题八:深入理解泛型可变性

  为了进一步说明泛型的协变和逆变是在C# 4.0中(C# 4.0即对于.net Framework 4.0)的版本都不支持泛型的协变和逆变,大家从MSDN中也可以发现的。下面是一张比较的截图(大家可以自己具体去MSDN上查看的, 当版本改为3.5或更低级的版本时,看下泛型的定义是不是没有out或in关键字,即之前的版本不支持泛型的可变性):

[C# 基础知识系列]专题八:深入理解泛型可变性

三、协变和逆变的注意事项

  并不是所有类型都支持泛型的协变和逆变的, 下面列出泛型的协变和你逆变中值得注意和明确的地方:

上一篇: [C# 基础知识系列]专题七: 泛型深入理解(一)
下一篇: C# Aop简单扫盲及ORM实体类属性拦截示例

1 2 下一页

关于我们 | 联系我们 | 加入我们 | 广告服务 | 投诉意见 | 网站导航
Copyright © 2000-2011 21tx.com, All Rights Reserved.
晨新科技 版权所有 Created by TXSite.net