IEnumerator <t> / IEnumerable <t>只暂时存储位置?</t> </t>

时间:2013-05-11 08:30:14

标签: c# foreach iterator ienumerable ienumerator

我正在开发一个图表,我需要将每个节点的内存使用量保持在尽可能低的水平。每个节点都实现IEnumerator / IEnumerable。

IEnumerator / IEnumerable在规范示例中使用“position”,这是迭代所需的持久游标值(例如,由foreach使用)。

我需要避免的是将此“位置”值存储在节点本身内部,因为这会增加每个字节数的开销。

如何构造节点类,以便临时对象存储此值 - 最好是在堆栈上 - 仅在迭代发生时,而不是作为节点本身的一部分?这可能吗?

2 个答案:

答案 0 :(得分:4)

通常,IEnumerable<T> 存储位置 - 只有IEnumerator<T>。 (迭代器块实现在实现两者方面都很奇怪,但它们肯定是异常的。)

我建议您采用与List<T>相同的方法:使用显式接口实现实现IEnumerable<T>,但也有一个返回自定义可变结构的公共方法(我知道可怕,但它 解决您的问题),其中包含对您的节点及其中的位置的引用。当您使用foreach进行迭代时,该结构值将仅存储在堆栈中(通常 - 假设您没有在迭代器块中执行此操作)。

正常的实现是为IEnumerator<T>创建一个可变的引用类型。通常情况下这是可以的,即使您同时有很多IEnumerable<T>个值,但您的IEnumerator<T>值很少。您是否关注活动对象的并发数量或垃圾收集?

答案 1 :(得分:4)

如果“node”是基础数据,那么将位置存储在节点中是非常不正确的,因为您可以拥有单独的枚举器。目前还不清楚你当前是如何实现这个API的,但是如果你使用一个位置作为局部变量的“迭代器块”,它将正确完成,但是在堆上。您还可以通过创建结构迭代器在堆栈上手动实现迭代器。重要的是公共GetEnumerator()作为结构类型返回,因此您需要为IEnumerable等使用显式接口实现。注意,直接通过Node的foreach将使用堆栈,但IEnumerable等将使用堆。

例如(使用基本链接列表):

using System;
using System.Collections;
using System.Collections.Generic;
class Program
{
    static void Main()
    {
        var list = new MyList<int>();
        list.Add(1);
        list.Add(2);
        list.Add(3);
        foreach (var i in list)
        { // this IS NOT using IEnumerable/IEnumerable<T>
            Console.WriteLine(i);
        }
    }
}
public class MyList<T> : IEnumerable<T>
{
    internal sealed class Node
    {
        private readonly T value;
        public Node Next { get; set; }
        public T Value { get { return value; } }
        public Node(T value) { this.value = value; }
    }
    Node root;
    public void Add(T value)
    {
        var newNode = new Node(value);
        var node = root;
        if (node == null) root = newNode;
        else
        {
            while (node.Next != null) node = node.Next;
            node.Next = newNode;
        }
    }
    public Enumerator GetEnumerator() { return new Enumerator(this); }
    IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
    IEnumerator<T> IEnumerable<T>.GetEnumerator() { return GetEnumerator(); }
    public struct Enumerator : IEnumerator, IEnumerator<T>
    {
        void IDisposable.Dispose() { node = null; list = null; }
        void IEnumerator.Reset() { node = null; }
        object IEnumerator.Current { get { return Current; } }
        private MyList<T> list;
        private Node node;
        public bool MoveNext()
        {
            if (node == null)
            {
                node = list.root;
                return node != null;
            }
            else
            {
                if (node.Next == null) return false;
                node = node.Next;
                return node != null;
            }
        }
        internal Enumerator(MyList<T> list) { this.list = list; node = null; }
        public T Current { get { return node == null ? default(T) : node.Value; } }
    }
}
相关问题