假设ClassA是一个随机类,我正在创建ClassA的SortedSet。我将枚举存储在字典中,但每当我尝试访问时,它总是给出null;
var set = new SortedSet<ClassA>();
set.Add(new ClassA());
var map = new Dictionary<int, SortedSet<ClassA>.Enumerator>();
map[1] = set.GetEnumerator();
map[1].MoveNext();
var val = map[1].Current;
//Why val is null ???发布于 2020-04-02 10:33:52
发生这种情况是因为SortedSet.Enumerator is a struct。每次使用字典的索引器来检索枚举器时,都会获得它的一个新副本。因此,即使您对该副本调用MoveNext(),下次获取枚举器副本时,它也没有任何Current的值。
有趣的是,由于the exact implementation of that struct中的一个怪癖,枚举器的每个副本都获得相同的引用类型对象来跟踪枚举的状态(堆栈),因此MoveNext()方法似乎可以工作(即,第一次调用它时它返回true,但以后任何时候都返回false )。
至少有四个选项可用于正确处理此…
将副本检索到一个变量中,并使用该变量而不是字典:
var set = new SortedSet<ClassA>();
set.Add(new ClassA());
var map = new Dictionary<int, SortedSet<ClassA>.Enumerator>();
map[1] = set.GetEnumerator();
var e = map[1];
e.MoveNext();
val = e.Current;请注意,在此示例中,枚举器的字典副本仍然不会具有所需的Current值。在调用MoveNext()之后,您必须再次设置字典的副本以保留它:map[1] = e;
使用数组而不是字典:
var set = new SortedSet<ClassA>();
set.Add(new ClassA());
var map = new SortedSet<ClassA>.Enumerator[2];
map[1] = set.GetEnumerator();
map[1].MoveNext();
var val = map[1].Current;除了map的声明和初始化的不同之外,它的工作方式与现在的代码完全相同。这是因为数组的索引元素是变量,而不是像索引语法那样通过索引器来处理任何其他集合。因此,您将直接对存储在数组中的枚举数的副本进行操作,而不是对索引器返回的新副本进行操作。
当然,只有当键值受到足够的约束,使得分配一个足够大的数组来容纳键的所有可能性时,这才能起作用。
声明一个引用类型的包装器以包含值类型枚举器:
class E<T>
{
public SortedSet<T>.Enumerator Enumerator;
public E(SortedSet<T>.Enumerator enumerator)
{
Enumerator = enumerator;
}
}然后是…
var set = new SortedSet<ClassA>();
set.Add(new ClassA());
var map = new Dictionary<int, E<ClassA>>();
map[1] = new E<ClassA>(set.GetEnumerator());
map[1].Enumerator.MoveNext();
val = map[1].Enumerator.Current;在本例中,字典只是返回对包装器对象的引用,确保枚举器只有一个副本(它存储在包装器对象中,而不是字典中)。因此,每次通过字典访问对象时,都会得到相同的副本。
当然,您最终必须通过包装器的Enumerator字段。这有点笨拙。但它会起作用的。
将枚举数存储在数组中,但通过字典进行索引:
var set = new SortedSet<ClassA>();
set.Add(new ClassA());
var map = new Dictionary<int, int>();
var a = new SortedSet<ClassA>.Enumerator[1];
map[1] = 0;
a[map[1]] = set.GetEnumerator();
a[map[1]].MoveNext();
val = a[map[1]].Current;这将混合前面的两个选项。数组用于存储实际的枚举数,以便您可以将它们作为变量进行寻址,但字典用作数组的一个间接级别,以便您可以通过任何您喜欢的键引用枚举数。
显然,在实际应用程序中,您可以通过枚举原始集合、将其枚举器存储在数组中并将原始键映射到字典中的数组索引来初始化数组。
额外的间接性有点笨拙,就像包装器选项一样,但没有那么糟糕,它解决了基于数组的选项的数组大小问题。
可以说,你的问题是这个问题的重复:List.All(e => e.MoveNext()) doesn't move my enumerators on
它肯定与那个密切相关,也与这个密切相关:Details on what happens when a struct implements an interface
https://stackoverflow.com/questions/60982735
复制相似问题