C# -- 泛型(3)泛型的协变与逆变

来源:互联网 发布:汉字译拼音软件 编辑:程序博客网 时间:2024/05/22 16:38

<泛型的协变逆变|泛型修饰符‘out’与‘in’>

|首先这2个拗口的名词先不用去管它,先知道协变和逆变主要是用在泛型的接口委托上就可以了,下面我们通过一个例子来看看:

|在这之前我们插点别的东西,我们知道接口是可以体现多态的,当然接口体现的多态注重功能上的多态,这和抽象类不同,抽象类更注重的是建立在血缘关系上的多态

知道接口是可以体现多态的之后,我们来看看一个相关的例子--

鸟和飞机都会飞,把飞定义成一个借口,在定义2个类

复制代码
    public interface IFlyable    {        void fly();    }    class Bird:IFlyable    {        public void fly()        {            Console.WriteLine("鸟儿飞!");        }    }    class Plane:IFlyable    {        public void fly()        {            Console.WriteLine("飞机飞!");        }    }
复制代码

 

下面看看接口体现的多态性:

复制代码
            IFlyable ifly;                         ifly = new Bird();            ifly.fly();            ifly = new Plane();            ifly.fly();
复制代码

运行结果:

鸟儿飞!

飞机飞!

 

了解了接口的多态性后我们再来看一个例子:

这里定义了2个类 Animal 和 Cat (Cat继承了Animal)

复制代码
    public class Animal    {    }    public class Cat:Animal    {    }
复制代码

 

继续往下看:

Cat cat = new Cat();

 

下面这句代码,cat向animal转,子类向父类转换,这时cat会隐式转换为animal 我们说“儿子像父亲” 这是完全可以理解的

Animal animal = cat;

 

但是 说”父亲像儿子“ 这是说不过去的 ,但是有的时候如果儿子坑爹强制转换了一下还是可以的

cat = (Cat)animal;

 

 

(协变)

            List<Cat> catArray = new List<Cat>();            List<Animal> animalArray = catArray;

 

如果是上面说的类,这样写是可以的,但是这里是会报错的  如图

继续往下看 这样写却可以

            IEnumerable<Cat> lCat = new List<Cat>();            IEnumerable<Animal> lAnimal = lCat;

 

对 IEnumerable<Cat> 转到定义 如图 我们发现这里多了一个 “out” 关键字

概念引入

1.对于泛型类型参数,out 关键字指定该类型参数是协变的。 可以在泛型接口和委托中使用 out 关键字。out keyword in generic interfaces and delegates." color="#0000ff">“协变”是指能够使用与原始指定的派生类型相比,派生程度更大的类型。

out keyword in generic interfaces and delegates." color="#0000ff">--对于 “协变” 笔者是这样理解的就是”说的通变化“ 就像 “儿子像父亲一样”(假定父亲派生程度0那么儿子的派生程度就是1了,所以父亲可以使用派生程度更大的儿子)

out keyword in generic interfaces and delegates." color="#0000ff">协变与多态性类似,因此它看起来非常自然。

out keyword in generic interfaces and delegates." color="#0000ff"> 

out keyword in generic interfaces and delegates." color="#0000ff">(逆变)

out keyword in generic interfaces and delegates." color="#0000ff">我们知道IComparable<T>接口中,T的修饰符是‘in’,下面我们修改一下上面的代码演示一下 

out keyword in generic interfaces and delegates." color="#0000ff"> 

out keyword in generic interfaces and delegates." color="#0000ff">复制代码
out keyword in generic interfaces and delegates." color="#0000ff">    class Cat : Animal, IComparable<Cat>    {        //仅演示        public int CompareTo(Cat other)        {            return 1;        }    }    class Animal : IComparable<Animal>    {        //仅演示        public int CompareTo(Animal other)        {            return 1;        }    }
out keyword in generic interfaces and delegates." color="#0000ff">复制代码

out keyword in generic interfaces and delegates." color="#0000ff"> 

out keyword in generic interfaces and delegates." color="#0000ff"> 

out keyword in generic interfaces and delegates." color="#0000ff">这里Cat和Animal都实现了IComparable<T>接口,然后我们这样写

out keyword in generic interfaces and delegates." color="#0000ff">            IComparable<Cat> ICat = new Cat();            IComparable<Animal> IAnimal = new Animal();            ICat = IAnimal;

out keyword in generic interfaces and delegates." color="#0000ff"> 

out keyword in generic interfaces and delegates." color="#0000ff">代码中ICat(高派生程度)使用 IAnimal(低派生程度) “父亲像儿子” 和上面的例子完全相反。

out keyword in generic interfaces and delegates." color="#0000ff"> 

out keyword in generic interfaces and delegates." color="#0000ff">概念引入:

out keyword in generic interfaces and delegates." color="#0000ff">2.对于泛型类型参数,in 关键字指定该类型参数是逆变的。 可以在泛型接口和委托中使用 in 关键字。“逆变”则是指能够使用派生程度更小的类型。

out keyword in generic interfaces and delegates." color="#0000ff">--对于 “逆变” 笔者的理解则是 “坑爹儿子” 反过来硬说 “父亲像儿子” 这是 “说不过去的” 只是利用了强硬的手段

out keyword in generic interfaces and delegates." color="#0000ff"> 

out keyword in generic interfaces and delegates." color="#0000ff"> 

out keyword in generic interfaces and delegates." color="#0000ff">在了解了上面的内容后,我们来看看“out” 与 “in” 关键字的特性

out keyword in generic interfaces and delegates." color="#0000ff">IEnumerable<T>接口的IEnumerator<T> GetEnumerator()方法返回了一个迭代器 ,不难发现T如果用 out 标记,则T代表了输出,也就说只能作为结果返回。

out keyword in generic interfaces and delegates." color="#0000ff">IComparable<T>接口的CompareTo(T other)方法传入了一个T类型的Other参数,不难发现T如果用 in 标记,则T代表了输入,也就是它只能作为参数传入。

out keyword in generic interfaces and delegates." color="#0000ff">下面我们演示一个例子

out keyword in generic interfaces and delegates." color="#0000ff">将动物会叫这功能,定义成一个泛型借口用out 修饰

out keyword in generic interfaces and delegates." color="#0000ff">

out keyword in generic interfaces and delegates." color="#0000ff">这里会出现一个错误

out keyword in generic interfaces and delegates." color="#0000ff">

out keyword in generic interfaces and delegates." color="#0000ff">把第二个带参数的setSound方法,去掉后编译可以正常通过

out keyword in generic interfaces and delegates." color="#0000ff">下面我们把 out 改成 in

out keyword in generic interfaces and delegates." color="#0000ff">

out keyword in generic interfaces and delegates." color="#0000ff">这里会出现一个错误

out keyword in generic interfaces and delegates." color="#0000ff">

out keyword in generic interfaces and delegates." color="#0000ff">把第一个setSound方法,去掉后编译可以正常通过,或者把第一个方法的返回值,改成其它非T类型,编译也可通过

out keyword in generic interfaces and delegates." color="#0000ff">这个演示充分说明了:out 修饰 T 则 T只能作为结果输出而不能作为参数  ; in 修饰 T 则 T只能作为参数而不能作为结果返回;

out keyword in generic interfaces and delegates." color="#0000ff"> 

out keyword in generic interfaces and delegates." color="#0000ff">------------------------------------------------------------------------------------------------------------------------------------------------------------

out keyword in generic interfaces and delegates." color="#0000ff"> 

out keyword in generic interfaces and delegates." color="#0000ff"> 

out keyword in generic interfaces and delegates." color="#0000ff"><IEnumerable接口及其泛型版>

out keyword in generic interfaces and delegates." color="#0000ff"> 为什么要用IEnumerable接口? 下面我们通过一个例子看看:

out keyword in generic interfaces and delegates." color="#0000ff">复制代码
out keyword in generic interfaces and delegates." color="#0000ff">    //定义Person类    public class Person    {        public Person(string _name)        {            this.name = _name;        }        public string name;    }    //定义People类    public class People    {        private Person[] _people;        public People(Person[] pArray)        {            //实例化数组 用于存Person实例            _people = new Person[pArray.Length];            for (int i = 0; i < pArray.Length; i++)            {                _people[i] = pArray[i];            }        }    }
out keyword in generic interfaces and delegates." color="#0000ff">复制代码

out keyword in generic interfaces and delegates." color="#0000ff">上面的代码我们定义了一个Person 类和一个 People 类,显然 People是用来存放多个Person实例的集合,下面我们尝试用Foreeach 遍历集合的每个元素 输出:

out keyword in generic interfaces and delegates." color="#0000ff">复制代码
out keyword in generic interfaces and delegates." color="#0000ff">        static void Main(string[] args)        {            Person[] personArray = new Person[3]{            new Person("Keiling1"),            new Person("Keiling2"),            new Person("Keiling3"),            };            People people = new People(personArray);            foreach (Person item in people)            {                Console.WriteLine(item.name);            }        }
out keyword in generic interfaces and delegates." color="#0000ff">复制代码

out keyword in generic interfaces and delegates." color="#0000ff">这里编译不能通过,出现了一个错误

out keyword in generic interfaces and delegates." color="#0000ff">

out keyword in generic interfaces and delegates." color="#0000ff">GetEnumerator:是IEnumerable接口中的一个方法,它返回一个 IEnumerator(迭代器),如下图 

out keyword in generic interfaces and delegates." color="#0000ff">

out keyword in generic interfaces and delegates." color="#0000ff">IEnumerator内部规定了,实现一个迭代器的所有基本方法,包括 如下图

out keyword in generic interfaces and delegates." color="#0000ff">

out keyword in generic interfaces and delegates." color="#0000ff">为了在foreach中使用 People的实例, 我们给People实现IEnumerable接口,代码如下:

out keyword in generic interfaces and delegates." color="#0000ff">复制代码
out keyword in generic interfaces and delegates." color="#0000ff">    public class People:IEnumerable    {        private Person[] _people;        public People(Person[] pArray)        {            //实例化数组 用于存Person实例            _people = new Person[pArray.Length];            for (int i = 0; i < pArray.Length; i++)            {                _people[i] = pArray[i];            }        }////IEnumerable和IEnumerator通过IEnumerable的GetEnumerator()方法建立了连接,可以通过IEnumerable的GetEnumerator()得到IEnumerator对象。        IEnumerator IEnumerable.GetEnumerator()        {            return (IEnumerator)GetEnumerator();        }        public PeopleEnum GetEnumerator()        {            return new PeopleEnum(_people);        }    }    public class PeopleEnum:IEnumerator    {        public Person[] _people;        public PeopleEnum(Person [] pArray)        {            _people = pArray;        }        //游标        int position = -1;        //是否可以往下 移        public bool MoveNext()        {            position++;            return (position < _people.Length);        }        //集合的所有元素取完了之后 重置position        public void Reset()        {            position = -1;        }        //实现 IEnumerator的 Current方法 返回当前所指的Person对象        object IEnumerator.Current        {            get            {                return Current;            }        }        //Current是返回Person类实例的只读方法        public Person Current        {            get            {                try                {                    return _people[position];                }                catch (IndexOutOfRangeException)                {                    throw new InvalidOperationException();                }            }        }    }
out keyword in generic interfaces and delegates." color="#0000ff">复制代码

out keyword in generic interfaces and delegates." color="#0000ff">测试运行:

out keyword in generic interfaces and delegates." color="#0000ff">复制代码
out keyword in generic interfaces and delegates." color="#0000ff">        static void Main(string[] args)        {            Person[] personArray = new Person[3]{            new Person("Keiling1"),            new Person("Keiling2"),            new Person("Keiling3"),            };            People people = new People(personArray);            foreach (Person item in people)            {                Console.WriteLine(item.name);            }        }
out keyword in generic interfaces and delegates." color="#0000ff">复制代码

out keyword in generic interfaces and delegates." color="#0000ff">结果:

out keyword in generic interfaces and delegates." color="#0000ff">

out keyword in generic interfaces and delegates." color="#0000ff">总结:

out keyword in generic interfaces and delegates." color="#0000ff">1.一个集合要支持foreach方式的遍历,必须实现IEnumerable接口,描述这类实现了该接口的对象,我们叫它 ‘序列’。

out keyword in generic interfaces and delegates." color="#0000ff">比如 List<T> 支持 foreach 遍历 是因为它实现了IEnumerable接口和其泛型版,如图--

out keyword in generic interfaces and delegates." color="#0000ff">

out keyword in generic interfaces and delegates." color="#0000ff"> 

out keyword in generic interfaces and delegates." color="#0000ff"> 

out keyword in generic interfaces and delegates." color="#0000ff">2. IEnumerator对象具体实现了迭代器(通过MoveNext(),Reset(),Current)。

out keyword in generic interfaces and delegates." color="#0000ff"> 

out keyword in generic interfaces and delegates." color="#0000ff">3. 从这两个接口的用词选择上,也可以看出其不同:IEnumerable是一个声明式的接口,声明实现该接口的class是“可枚举(enumerable)”的,但并没有说明如何实现迭代器,

out keyword in generic interfaces and delegates." color="#0000ff">而IEnumerator是一个实现式的接口,IEnumerator对象就是一个迭代器。 

out keyword in generic interfaces and delegates." color="#0000ff"> 

out keyword in generic interfaces and delegates." color="#0000ff">关于IEnumerable<T>我们来了解一下它的代码:

out keyword in generic interfaces and delegates." color="#0000ff"> 

out keyword in generic interfaces and delegates." color="#0000ff">4.由于IEnumerable<T>继承了IEnumerable接口,所以要实现IEnumerator<T> ,还需要实现IEnumerator接口,由于和泛型版本的方法同名,所以该方法的实现需要使用显式接口实现。这里就不继续介绍它的具体实现了,和IEnumerator基本一致,这里就不详述了,读者可以自己动手写一下。
out keyword in generic interfaces and delegates." color="#0000ff"> 
out keyword in generic interfaces and delegates." color="#0000ff">ps 了解IEnumerable和IEnumerable<T>对今后学西理解LINQ是有很大帮助的。

out keyword in generic interfaces and delegates." color="#0000ff"> 

out keyword in generic interfaces and delegates." color="#0000ff"> 

out keyword in generic interfaces and delegates." color="#0000ff">
0 0
原创粉丝点击