我正在开发一个图表,我需要将每个节点的内存使用量保持在尽可能低的水平。每个节点都实现 IEnumerator / IEnumerable。
IEnumerator / IEnumerable 在规范示例中使用“位置”,这是迭代所需的持久光标值(例如,由 使用foreach
)。
我需要避免的是将此“位置”值在内部存储到节点本身,因为这会增加每个字节的开销。
如何构造节点类,以便临时对象存储该值 - 最好在堆栈上 - 仅在迭代发生时,而不是作为节点本身的一部分?这可能吗?
我正在开发一个图表,我需要将每个节点的内存使用量保持在尽可能低的水平。每个节点都实现 IEnumerator / IEnumerable。
IEnumerator / IEnumerable 在规范示例中使用“位置”,这是迭代所需的持久光标值(例如,由 使用foreach
)。
我需要避免的是将此“位置”值在内部存储到节点本身,因为这会增加每个字节的开销。
如何构造节点类,以便临时对象存储该值 - 最好在堆栈上 - 仅在迭代发生时,而不是作为节点本身的一部分?这可能吗?
如果“节点”是基础数据,那么将位置存储在节点中是非常不正确的,因为您应该能够拥有单独的枚举器。目前尚不清楚您当前如何实现此 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; } }
}
}
通常 anIEnumerable<T>
不存储位置 - 只有 an IEnumerator<T>
。(迭代器块的实现在实现两者方面都很奇怪,但它们绝对是异常的。)
我建议您采用相同的方法List<T>
:IEnumerable<T>
使用显式接口实现实现,但也有一个公共方法返回一个自定义可变结构(我知道很糟糕,但它确实解决了您的问题),其中包含对您的节点和位置的引用在其中。当您迭代 usingforeach
时,该结构值将仅存储在堆栈中(通常 - 例如,假设您没有在迭代器块中执行此操作)。
正常的实现是为IEnumerator<T>
. 通常这没关系,因为即使您同时拥有很多IEnumerable<T>
价值观,但您的IEnumerator<T>
价值观却很少。您是否关心活动对象的并发数量或垃圾收集?