gpt4 book ai didi

c# - SortedDictionary.ValueCollection 的枚举器行为不同于其他枚举器

转载 作者:行者123 更新时间:2023-12-04 17:51:27 24 4
gpt4 key购买 nike

由于 SortedDictionary.ValueCollectionEnumerator 行为与其他枚举器不同,我们最近遇到了一个错误。我已经设法将问题缩小到以下(无意义的)示例:

public void Example()
{
var sorted = new SortedDictionary<string, int>
{
{"1", 1 },
{"3", 3 },
{"0", 0 },
{"2", 2 },
{"4", 4 }
};

var fromValues = sorted.Values.GetEnumerator();
fromValues.MoveNext();

var fromLinq = sorted.Select(x => x.Value).GetEnumerator();
fromLinq.MoveNext();

var fromDictionary = new Dictionary<string, int>(sorted).Values.GetEnumerator();
fromDictionary.MoveNext();

for (var i = 0; i < 3; i++)
{
Console.WriteLine($"Printing for {i}");
Print(" From Values: ", fromValues, i);
Console.WriteLine(" ------------");
Print(" From Linq: ", fromLinq, i);
Console.WriteLine(" ------------");
Print(" From Dictionary: ", fromDictionary, i);
Console.WriteLine();
}
}

private void Print(string prefix, IEnumerator<int> enumerator, int value)
{
do
{
Console.WriteLine(prefix + "Value in loop: " + enumerator.Current);
if (enumerator.Current == value)
{
Console.WriteLine(prefix + "Selected Value: " + enumerator.Current);
break;
}
} while (enumerator.MoveNext());
}

这将产生以下输出:

Printing for 0
From Values: Value in loop: 0
From Values: Selected Value: 0
------------
From Linq: Value in loop: 0
From Linq: Selected Value: 0
------------
From Dictionary: Value in loop: 0
From Dictionary: Selected Value: 0

Printing for 1
From Values: Value in loop: 0
From Values: Value in loop: 1
From Values: Selected Value: 1
------------
From Linq: Value in loop: 0
From Linq: Value in loop: 1
From Linq: Selected Value: 1
------------
From Dictionary: Value in loop: 0
From Dictionary: Value in loop: 1
From Dictionary: Selected Value: 1

Printing for 2
From Values: Value in loop: 0
From Values: Value in loop: 2
From Values: Selected Value: 2
------------
From Linq: Value in loop: 1
From Linq: Value in loop: 2
From Linq: Selected Value: 2
------------
From Dictionary: Value in loop: 0
From Dictionary: Value in loop: 1
From Dictionary: Value in loop: 2
From Dictionary: Selected Value: 2

如您所见,三个 Iterators 的行为不同:

  • SortedDictionary:Current 在传递给函数时始终为 0,但 MoveNext 在循环中推送到正确的值。
  • Linq:表现出我对 Iterator 的预期。
  • 字典:每次将 Enumerator 传递给函数时重置。

我怀疑 SortedDictionary.ValueCollection.Enumerator 是一个结构并且由 linq 生成的是一个引用类型这一事实与它有关。但这并不能解释为什么它不像 Dictionary 中的 Enumerator 那样工作。

最佳答案

我相信答案是 SortedDictionary值枚举器是一个结构(类型为 System.Collections.Generic.SortedDictionary<string,int>.ValueCollection.Enumerator )。

发生的事情是,每次将结构传递给 Print() 时都会复制该结构。方法,因此该方法始终使用原始枚举器的副本 - 这会导致工作异常。

下面的程序演示了这一点:

using System;
using System.Collections.Generic;

class Program
{
static void Main()
{
Example();
}

public static void Example()
{
var sorted = new SortedDictionary<string, int>
{
{"1", 1 },
{"3", 3 },
{"0", 0 },
{"2", 2 },
{"4", 4 }
};

var fromValues1 = sorted.Values.GetEnumerator();
// fromValue1 type is struct System.Collections.Generic.SortedDictionary<string,int>.ValueCollection.Enumerator
fromValues1.MoveNext();

while (printNextValue(fromValues1)) // Prints 0 once for each value in the dictionary.
;

Console.WriteLine("-----------------");

IEnumerator<int> fromValues2 = sorted.Values.GetEnumerator();
// fromValues2 type is boxed struct System.Collections.Generic.SortedDictionary<string,int>.ValueCollection.Enumerator
fromValues2.MoveNext();

while (printNextValue(fromValues2)) // Prints each value in the dictionary.
;
}

static bool printNextValue(IEnumerator<int> enumerator)
{
Console.WriteLine(enumerator.Current);
return enumerator.MoveNext();
}
}

第一个循环输出全零,而第二个循环输出正确的值。

此示例中两个循环之间的唯一区别是第一个迭代器声明为:

var fromValues1 = sorted.Values.GetEnumerator();

第二个声明为:

IEnumerator<int> fromValues2 = sorted.Values.GetEnumerator(); .

第一个声明将导致 fromValues1是一个结构,而第二个是一个盒装结构。

因为结构是装箱的,这意味着它在传递给 printNextValue() 时不会被复制,这意味着 printNextValue()将使用原始枚举器而不是它的副本。

但是,这并不能解释循环终止的原因!如果每次都复制原始枚举器位置 printNextValue()被调用,那么循环永远不会终止,因为原始枚举器的位置永远不会更新。

这让我相信 SortedDictionary.Enumerator 的实现有些复杂意味着当复制结构时,它的一些数据会被复制,但有些不会。

(查看源代码,我怀疑这是由于枚举器实现的 Current 是一个被复制的值类型,但是 MoveNext() 似乎操纵一个堆栈 - 作为一个引用类型 - 在两者之间共享枚举器的所有副本。但是,代码有点太复杂了,我目前有限的时间无法分析...)

关于c# - SortedDictionary.ValueCollection 的枚举器行为不同于其他枚举器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60005524/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com