设为首页收藏本站Access中国

Office中国论坛/Access中国论坛

 找回密码
 注册

QQ登录

只需一步,快速开始

返回列表 发新帖
查看: 1298|回复: 2
打印 上一主题 下一主题

【转载】关于IEnumerable 是否应该继承IEnumerable(老外讨论的很多)

[复制链接]
跳转到指定楼层
1#
发表于 2014-2-24 11:30:49 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
关于IEnumerable <T>是否应该继承IEnumerable,老外讨论的很多!

http://blogs.msdn.com/brada/archive/2005/01/18/355755.aspx
http://morganchengmo.spaces.live ... 8939932E!1458.entry

Should IEnumerable <T> inherits from IEnumerable?

.Net 2.0引入了Generics(泛型)的概念,原有的在System.Collectionis namespace下的接口几乎都在System.Collections.Generic namesapce下多了一个对应的Generic接口,不过奇怪的是IList <T>不是继承自IList的,而IEnumerable <T>是继承自IEnumerable的。

Brad Abrams有一篇Blog解释了为什么,但是实际上他只解释了为什么IList <T>没有继承IList。以他的观点,理想状况下,所有的新的支持Generic的 interface都应该继承以前对应的interface,但是如果让IList <T>继承IList的话,那么是实现 IList <int>的类就需要实现两个Insert方法,一个是IList <int>的void Insert(int index, int item),另外一个是IList的void Insert(int index, object item),这样对一个类对象obj,调用obj.Insert(0, 123)和调用obj.Insert(0, "abc")都是合法的,这不是我们想要的,因为我们既然继承IList <int>,就希望编译器能够做好类型检查,不让非int的物体插进来,但是这个继承关系造成了这样局面,所以IList <T>不继承IList。

上面IList <T>不应继承 IList的理由很充分,但是并不足以说明IEnumerable <T>就该继承IEnumerable。当然,因为T对 IEnumerable而言,只有“输出”作用,不像IList一样既有“输入”作用,也有“输出”作用,所以安全,但是,比较不爽的就是,每个实现 IEnumerable <T>的类,不得不实现两个GetEnumerator,MSDN的sample code并没有强调这一点,弄得很多新手(包括我:)一开始只实现了IEnumerable <T>.GetEnumerator,被编译器的出错提示搞得莫名其妙。

我觉得就不应该让IEnumerable <T>继承IEnumerable,虽然这个继承关系似乎不违反LSP,但是我觉得LSP只是一个必要条件,不是充分条件。如果谁想让一个类 既能是IEnumerable,又能是 IEnumerable <T>,那就同时实现这两个interface好了,就和IList <T>一样:

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 分享淘帖 订阅订阅
2#
 楼主| 发表于 2014-2-24 11:31:08 | 只看该作者
[size=14.399999618530273px]加一点测试用代码:
[size=14.399999618530273px]

[size=14.399999618530273px]
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. public class GenericList<T> : System.Collections.Generic.IEnumerable<T>
  5. {
  6.     protected Node head;
  7.     protected Node current = null;
  8.     protected class Node
  9.     {
  10.         public Node Next { get; set; }
  11.         public T Data { get; set; }
  12.         public Node(T t)
  13.         {
  14.             Next = null;
  15.             Data = t;
  16.         }
  17.     }
  18.     public GenericList()
  19.     {
  20.         head = null;
  21.     }
  22.    
  23.     public void Add(T t)
  24.     {
  25.         Node n = new Node(t);
  26.         n.Next = head;
  27.         head = n;
  28.     }
  29.     IEnumerator<T> IEnumerable<T>.GetEnumerator()//显式接口定义
  30.     //public IEnumerator<T> GetEnumerator()//隐式接口定义
  31.     {
  32.         Console.WriteLine("开始执行:IEnumerable<T>");
  33.         Node current = head;
  34.         while (current != null)
  35.         {
  36.             yield return current.Data;
  37.             current = current.Next;
  38.         }
  39.     }
  40.     //IEnumerator GetEnumerator()//隐式接口定义
  41.     IEnumerator IEnumerable.GetEnumerator()//显式接口定义
  42.     {
  43.         //return GetEnumerator();
  44.         //return IEnumerable<T>.GetEnumerator();
  45.         Console.WriteLine("开始执行:IEnumerable");
  46.         Node current = head;
  47.         while (current != null)
  48.         {
  49.             yield return current.Data;
  50.             current = current.Next;
  51.         }
  52.     }
  53. }
  54. class MyClass
  55. {
  56.     static void Main(string[] args)
  57.     {
  58.         GenericList<int> list = new GenericList<int> { 1, 2, 3, };
  59.         //定义在类上
  60.         GenericList<int> ieC = list;
  61.         foreach (var jC in ieC)
  62.         {
  63.             Console.WriteLine(jC);
  64.         }
  65.         //定义在显式接口上
  66.         IEnumerable<int> ieT = list;
  67.         foreach (var jT in ieT)
  68.         {
  69.             Console.WriteLine(jT);
  70.         }
  71.         //定义在隐式接口上
  72.         IEnumerable ie = list;
  73.         foreach (var j in ie)
  74.         {
  75.             Console.WriteLine(j);
  76.         }
  77.     }
  78. }

  79. //T Current { get; }//IEnumerable<T>专有
  80. //object Current { get; }//IEnumerable专有
  81. //bool MoveNext();
  82. //void Reset();
  83. //为什么新增加的泛型接口IEnumerable<T>要继承IEnumerable,这是为了兼容。
  84. //理论上所有的泛型接口都要继承自所有的非泛型接口。
  85. //例如在.net 1.1中有个方法接收的是IEnumerable类型的参数,
  86. //当移植到新的环境下,我们传入一个IEnumerable<T>的参数,
  87. //它也是可以被接受的,因为他们完成的都是枚举的行为。
  88. //然而特殊的是IList<T>没有继承自IList接口,
  89. //因为如果让IList<T>继承 IList的话,
  90. //那么是实现IList<int>的类就需要实现两个Insert方法,
  91. //一个是IList<int>的void Insert(int index, int item),
  92. //另外一个是IList的void Insert(int index, object item),
  93. //这是就有一个接口可以把object类型的数据插入到IList<int>集合中了,
  94. //这是不对的,所以不继承。
  95. //而IEnumerable<T>不同的是,它只有”输出“的作用,
  96. //也就是说我们只会从它里面取数据,
  97. //所以不会有上面描述的混乱出现。
  98. //.NET Framework使用IEnumerable<T>表示一个关系(集合),但反之不然。
  99. //IEnumerable<T>的本质是一个序列枚举器,本身就具备三重特性,集合、关系、序列。
复制代码



3#
 楼主| 发表于 2014-2-24 11:31:20 | 只看该作者
/T Current { get; }//IEnumerable<T>专有  
//object Current { get; }//IEnumerable专有  
//bool MoveNext();  
//void Reset();  
//为什么新增加的泛型接口IEnumerable<T>要继承IEnumerable,这是为了兼容。  
//理论上所有的泛型接口都要继承自所有的非泛型接口。  
//例如在.net 1.1中有个方法接收的是IEnumerable类型的参数,  
//当移植到新的环境下,我们传入一个IEnumerable<T>的参数,  
//它也是可以被接受的,因为他们完成的都是枚举的行为。  
//然而特殊的是IList<T>没有继承自IList接口,  
//因为如果让IList<T>继承 IList的话,  
//那么是实现IList<int>的类就需要实现两个Insert方法,  
//一个是IList<int>的void Insert(int index, int item),  
//另外一个是IList的void Insert(int index, object item),  
//这是就有一个接口可以把object类型的数据插入到IList<int>集合中了,  
//这是不对的,所以不继承。  
//而IEnumerable<T>不同的是,它只有”输出“的作用,  
//也就是说我们只会从它里面取数据,  
//所以不会有上面描述的混乱出现。  
//.NET Framework使用IEnumerable<T>表示一个关系(集合),但反之不然。  
//IEnumerable<T>的本质是一个序列枚举器,本身就具备三重特性,集合、关系、序列。  
您需要登录后才可以回帖 登录 | 注册

本版积分规则

QQ|站长邮箱|小黑屋|手机版|Office中国/Access中国 ( 粤ICP备10043721号-1 )  

GMT+8, 2024-5-8 07:41 , Processed in 0.088801 second(s), 26 queries .

Powered by Discuz! X3.3

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表