当前位置:软件学习 > Word >>

C# Keywords Series 6 in(Generic Modifier)&out(Generic Modifier)

 本篇文章主要讲述 in 和 out 两个泛型参数修饰符。对于这两个关键字,楼主也是各种不懂,要学会使用这两个关键字,必须先理解协变和逆变的概念。
 
  协变和逆变
 
  简单定义就是:协变就是泛型接口从子类向父类转化,逆变就是父类向子类转换。下面两种转换就是协变和逆变:
 
[csharp]  
IEnumerable<string> strs = null;  
IEnumerable<object> objs = null;  
objs = strs;    // “子类”向“父类”转换,即泛型接口的协变  
strs = objs;    // “父类”向“子类”转换,即泛型接口的逆变,这个会报错,IEnumerable 接口用 out 修饰泛型参数,限定了只能协变,这里只是为了说明两个转换的区别  
 
  上面的代码,IEnumerable<string> 和 IEnumerable<object> 表面上看有点类似父类和子类的关系,实际上他们根本没有任何继承关系,可以认为是两个独立的类。而 .NET 4.0开始,有条件的允许上面协变和逆变的转换,这里就用到 in 或 out 关键字修饰泛型参数 T 的使用范围。
 
  可以看下 IEnumerable 的声明
 
 
 
[csharp]  
[TypeDependency("System.SZArrayHelper")]  
   public interface IEnumerable<out T> : IEnumerable  
   {  
  
       IEnumerator<T> GetEnumerator();  
   }  
 
 
 
  如果 IEnumerable 接口用 in 修饰泛型参数的话,那么逆变(即 strs = objs;) 是可以通过编译的。所以我们总结起来就是: 用 out 来修饰泛型参数时允许协变,用 in来修饰泛型参数允许逆变。
 
  说到这里,这两个关键字倒没什么讲的,那为什么.NET 4.0开始引入协变逆变。
 
[csharp]  
List<object> list = new List<object>();  
list.AddRange(strs); // 如果没有协变(子类对于父类的隐式转换),这行代码会报错  
list.AddRange(objs);   
 
  上面代码,如果IEnumerable 接口没有用 out 修饰泛型参数,那就会编译报错。而且你得写个循环,将 strs 一个个转换为 IEnumerable<object>,为了避免这样的麻烦,支持协变的话,就可以直接转换,不会编译报错。所有协变逆变的问题实际上都源于一个根本的原则: 子类可以向父类隐式转换,父类不能向子类隐式转换。
 
  最后再看一个完整的例子,来看看 in 和 out 的使用方法:
 
[csharp]  
class Program  
{  
    static void Main(string[] args)  
    {  
        IContravariant<Object> iobj1 = new Sample<Object>();  
        IContravariant<String> istr1 = new Sample<String>();  
        ICovariant<Object> iobj2 = new Sample<Object>();  
        ICovariant<String> istr2 = new Sample<String>();  
        istr1 = iobj1;  // in 修饰符实现协变(子类对于父类的隐式转换)  
        iobj2 = istr2;  // out 修饰符实现逆变(父类对于子类的隐式转换)   
    }  
}  
  
// 逆变接口.  
interface IContravariant<in A> { }  
  
// 协变接口.  
interface ICovariant<out R> { }  
  
// 实现协变逆变接口的类.  
class Sample<A> : IContravariant<A> ,ICovariant<A>  
{ }  
  
   
 
补充:软件开发 , C# ,
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,