2

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

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

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

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

4

2 回答 2

4

如果“节点”是基础数据,那么将位置存储在节点中是非常不正确的,因为您应该能够拥有单独的枚举器。目前尚不清楚您当前如何实现此 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; } }
    }
}
于 2013-05-11T08:37:36.407 回答
4

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

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

正常的实现是为IEnumerator<T>. 通常这没关系,因为即使您同时拥有很多IEnumerable<T>价值观,但您的IEnumerator<T>价值观却很少。您是否关心活动对象的并发数量或垃圾收集?

于 2013-05-11T08:34:40.140 回答