我正在开发一个图表,我需要将每个节点的内存使用量保持在尽可能低的水平。每个节点都实现IEnumerator / IEnumerable。
IEnumerator / IEnumerable在规范示例中使用“position”,这是迭代所需的持久游标值(例如,由foreach
使用)。
我需要避免的是将此“位置”值存储在节点本身内部,因为这会增加每个字节数的开销。
如何构造节点类,以便临时对象存储此值 - 最好是在堆栈上 - 仅在迭代发生时,而不是作为节点本身的一部分?这可能吗?
答案 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; } }
}
}