在C#中,IEnumerable如何在内存中存储?

5

就像数组是顺序内存分配一样,列表可能与链表以相同的方式存储在内存中(如果我错了,请纠正我)。在C#中,IEnumerable如何存储在内存中?假设我有一个类。

 public class Employee
 {
      public int Id { get; set; }
      public string Name { get; set; }
 }

以下两种情况下内存分配将有所不同。为什么编译器不允许我们编辑IEnumerables?
IList<Employee> EmpList ;

Or

IEnumerables<Employee> EmpList ;

7
IEnumerable是一个接口。它的实现取决于具体的实现方式。ArrayLinkedList也都是IEnumerable,但正如你指出的那样,它们在内存存储方面是不同的。 - user2160375
1
可以是其中之一,也可能是其他的,这取决于具体情况。接口本身并不意味着任何事情。 - Lasse V. Karlsen
2
无法使用 IEnumerable<T> 来存储集合,因为接口 IEnumerable<T> 不允许插入元素,它只允许读取。 - Codor
1
@Codor 不对。List<T> 实现了 IEnumerable<T>,但是列表绝对是集合。我会说 IEnumerable<T> 并没有暴露插入接口,但它可以(但不一定,例如机器状态)包含集合。 - user2160375
1
@shiv - 再次说明,IList<T>IEnumerable<T>只是接口。实际实现取决于项的存储方式。例如,List<T>(它同时实现了这两个接口)恰好将项存储在底层数组中。但这只是一个实现细节,未来可能会改变。- 此外,对于所有引用类型,只有引用被“存储”在底层数组中。实际对象数据可以分布在任何地方,随机分布。 - Corak
显示剩余8条评论
3个回答

6
一个IEnumerable变量存储一个对象引用(实现细节是四个或八个字节,具体取决于进程)。这同样适用于System.Collections.Generic.List变量、数组变量、ICollection变量,或任何引用类型变量(尽管与问题无关)。
对象的枚举器产生的数据将按照引用指向的对象所规定的方式进行存储。在某些情况下,它将仅存储第一个元素以及一些信息,用于枚举器计算后续元素(例如System.Linq.Enumerable.Range)。在其他情况下,对象可以是数组或S.C.G.List(顺序存储-列表使用数组进行存储),也可以是链表,哈希集或排序集(分别使用哈希表和二叉树),或者任何其他人想象和实现的东西。
在您提出的有关内存分配的问题中,这些变量之间的内存使用没有区别。
IList<Employee> empList = new List<Employee>();

并且

IEnumerable<Employee> empList = new List<Employee>();

这两者的区别在于,你可以从empList对象引用中调用哪些方法。在第一种情况下,你只能使用IList和它所继承的接口中定义的成员来限制集合的变化。而在第二种情况下,你只能调用GetEnumerator方法,因此无法改变集合。

6

这个问题无法回答。

这取决于实现 IEnumerable 的底层对象。

它可能是一个数组,在这种情况下,内存表示只是一个数组,或者它可能是一个懒惰实现的枚举器,在需要时产生值,在这种情况下,除了该方法中状态机的局部变量之外,它实际上没有内存表示。


2
"IEnumerable" 是一个接口,它应该做的就是:

http://msdn.microsoft.com/en-us/library/cc317868.aspx

提供一个枚举器:
 public IEnumerator GetEnumerator() 

枚举器的实现可能是不同的,事实上,它甚至可能根本不存储任何数据。
  // Generates 0, 1, 2, ... sequence
  public sealed class Sample: IEnumerable {
    public IEnumerator GetEnumerator() {
      for(int i = 0;; ++i)
        yield return i;
    }  
  }

2
从技术上讲,这至少在编译器生成的枚举器实例上存储了一个 i.Current 值... ;p - Marc Gravell

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接