IEnumerator<XElement> 行为
IEnumerator<XElement> behaviour
我使用以下程序枚举 xml 输入中的元素。当到达最后一个元素时
x.MoveNext()
returns false,这部分很清楚。我的问题是:
- 为什么它继续打印
<c>
即使列表已用完
为什么 x.Current
永远不等于 null?这是我所期望的,因为 XElement
是引用类型
static void Main(string[] args)
{
var x = GetXml();
while (true)
{
var isMoving = x.MoveNext();
Console.WriteLine($"Cursor moved: {isMoving}");
Console.WriteLine($"Current element is null: {x.Current == null}");
Console.WriteLine($"Current element: {x.Current}" );
}
}
static IEnumerator<XElement> GetXml()
{
return XElement.Parse(@"<x>
<a></a>
<b></b>
<c></c>
</x>").Elements().GetEnumerator();
}
输出:
Cursor moved: true
Current element is null: False
Current element: <a></a>
Cursor moved: true
Current element is null: False
Current element: <b></b>
Cursor moved: true
Current element is null: False
Current element: <c></c>
Cursor moved: False <- confusing bit
Current element is null: False
Current element: <c></c>
Why does x.Current never equal null?
简短回答:正如 Brian Kernighan 所说,"your compiler is the final authority on the language"1。它之所以能如此,是因为微软的人就是这样写代码的。正如我们将在下面看到的,在这种情况下,这取决于实施者。用传奇计算机科学家 Ray Dorset2, "just do what you feel".
的话来说
不过这里还是有些有趣的地方。 According to MSDN、
If the last call to MoveNext returned false, Current is undefined.
这是通用的 IEnumerator<T>
,您正在使用的那个。这是你问题的答案。
在某些语言中,例如JavaScript,"undefined"是关键字。在其他情况下,它是一个简单的英语单词,用于描述规范留下的内容。如果他们说 C 或 C# 中的行为是 "undefined",他们的意思是这取决于实现者。
以上不同于 nongeneric IEnumerator
的记录行为:
If the last call to MoveNext returned false, calling Current throws an exception.
所有它告诉你的是,使用泛型 IEnumerator<T>
,在 MoveNext()
return 为 false 之后,Current
可以做实现者想要做的任何事情。它可以 return default(T)
(这就是我要做的),但它可以 return 最后一项。我会犹豫是否让它抛出异常,因为我们有不止一个不抛出的 Microsoft .NET 代码(你找到的那个,还有几个你将要看到的我的代码),并且MSDN 并没有说 可能 ,所以就当它不是。
public class Program
{
public static void Main()
{
//var x = GetEnumeratorNonGeneric(1);
//var x = GetEnumerator(1);
//var x = GetEnumeratorInt(1);
//var x = GetEnumeratorIntRangeSelect(1);
var x = GetEnumeratorIntList(1);
for (int i = 0; i < 2; ++i)
{
var isMoving = x.MoveNext();
Console.WriteLine($"Cursor moved: {isMoving}");
Console.WriteLine($"Current element is null: {x.Current == null}");
Console.WriteLine($"Current element: {x.Current}" );
}
}
static IEnumerator<String> GetEnumerator(int count)
{
return Enumerable.Range(1, count).Select(n => n.ToString()).GetEnumerator();
}
static IEnumerator<int> GetEnumeratorInt(int count)
{
return Enumerable.Range(1, count).GetEnumerator();
}
static IEnumerator<int> GetEnumeratorIntRangeSelect(int count)
{
return Enumerable.Range(1, count).Select(n => n).GetEnumerator();
}
static IEnumerator<int> GetEnumeratorIntList(int count)
{
return Enumerable.Range(1, count).ToList().GetEnumerator();
}
static IEnumerator GetEnumeratorNonGeneric(int count)
{
return new ArrayList(Enumerable.Range(1, count).Select(n => n.ToString()).ToArray()).GetEnumerator();
}
}
GetEnumeratorNonGeneric()
的输出:
Cursor moved: True
Current element is null: False
Current element: 1
Cursor moved: False
Run-time exception (line -1): Enumeration already finished.
Stack Trace:
[System.InvalidOperationException: Enumeration already finished.]
来自 GetEnumerator()
的输出。这不会抛出,但在这种情况下会 return default(T)
, null
。
Cursor moved: True
Current element is null: False
Current element: 1
Cursor moved: False
Current element is null: True
Current element:
来自 GetEnumeratorInt()
的输出。 Enumerable.Range()
returns 一个枚举器,它使用 Current
的最后一个值:
Cursor moved: True
Current element is null: False
Current element: 1
Cursor moved: False
Current element is null: False
Current element: 1
使用 GetEnumeratorIntRangeSelect()
,我们发现 Select()
为我们提供了一个枚举器,其 Current
returns default(T)
在 MoveNext()
[=92 之后=] 是错误的。这对于上面的 GetEnumerator()
来说确实是多余的,但它说明了值类型而不是引用类型会发生什么。
Cursor moved: True
Current element is null: False
Current element: 1
Cursor moved: False
Current element is null: False
Current element: 0
我们通过调用 ToList()
获得的 List<T>
枚举器也在结束后从 Current
returns default(T)
获得。
我没有找到在结束后引发 Current
的通用 IEnumerator<T>
,但如您所见,我并没有找到一个很好的项目。
1 或者类似的话;我找不到确切的措辞。
2 可能不是真正的计算机科学家。
我使用以下程序枚举 xml 输入中的元素。当到达最后一个元素时
x.MoveNext()
returns false,这部分很清楚。我的问题是:
- 为什么它继续打印
<c>
即使列表已用完 为什么
x.Current
永远不等于 null?这是我所期望的,因为XElement
是引用类型static void Main(string[] args) { var x = GetXml(); while (true) { var isMoving = x.MoveNext(); Console.WriteLine($"Cursor moved: {isMoving}"); Console.WriteLine($"Current element is null: {x.Current == null}"); Console.WriteLine($"Current element: {x.Current}" ); } } static IEnumerator<XElement> GetXml() { return XElement.Parse(@"<x> <a></a> <b></b> <c></c> </x>").Elements().GetEnumerator(); }
输出:
Cursor moved: true
Current element is null: False
Current element: <a></a>
Cursor moved: true
Current element is null: False
Current element: <b></b>
Cursor moved: true
Current element is null: False
Current element: <c></c>
Cursor moved: False <- confusing bit
Current element is null: False
Current element: <c></c>
Why does x.Current never equal null?
简短回答:正如 Brian Kernighan 所说,"your compiler is the final authority on the language"1。它之所以能如此,是因为微软的人就是这样写代码的。正如我们将在下面看到的,在这种情况下,这取决于实施者。用传奇计算机科学家 Ray Dorset2, "just do what you feel".
的话来说不过这里还是有些有趣的地方。 According to MSDN、
If the last call to MoveNext returned false, Current is undefined.
这是通用的 IEnumerator<T>
,您正在使用的那个。这是你问题的答案。
在某些语言中,例如JavaScript,"undefined"是关键字。在其他情况下,它是一个简单的英语单词,用于描述规范留下的内容。如果他们说 C 或 C# 中的行为是 "undefined",他们的意思是这取决于实现者。
以上不同于 nongeneric IEnumerator
的记录行为:
If the last call to MoveNext returned false, calling Current throws an exception.
所有它告诉你的是,使用泛型 IEnumerator<T>
,在 MoveNext()
return 为 false 之后,Current
可以做实现者想要做的任何事情。它可以 return default(T)
(这就是我要做的),但它可以 return 最后一项。我会犹豫是否让它抛出异常,因为我们有不止一个不抛出的 Microsoft .NET 代码(你找到的那个,还有几个你将要看到的我的代码),并且MSDN 并没有说 可能 ,所以就当它不是。
public class Program
{
public static void Main()
{
//var x = GetEnumeratorNonGeneric(1);
//var x = GetEnumerator(1);
//var x = GetEnumeratorInt(1);
//var x = GetEnumeratorIntRangeSelect(1);
var x = GetEnumeratorIntList(1);
for (int i = 0; i < 2; ++i)
{
var isMoving = x.MoveNext();
Console.WriteLine($"Cursor moved: {isMoving}");
Console.WriteLine($"Current element is null: {x.Current == null}");
Console.WriteLine($"Current element: {x.Current}" );
}
}
static IEnumerator<String> GetEnumerator(int count)
{
return Enumerable.Range(1, count).Select(n => n.ToString()).GetEnumerator();
}
static IEnumerator<int> GetEnumeratorInt(int count)
{
return Enumerable.Range(1, count).GetEnumerator();
}
static IEnumerator<int> GetEnumeratorIntRangeSelect(int count)
{
return Enumerable.Range(1, count).Select(n => n).GetEnumerator();
}
static IEnumerator<int> GetEnumeratorIntList(int count)
{
return Enumerable.Range(1, count).ToList().GetEnumerator();
}
static IEnumerator GetEnumeratorNonGeneric(int count)
{
return new ArrayList(Enumerable.Range(1, count).Select(n => n.ToString()).ToArray()).GetEnumerator();
}
}
GetEnumeratorNonGeneric()
的输出:
Cursor moved: True
Current element is null: False
Current element: 1
Cursor moved: False
Run-time exception (line -1): Enumeration already finished.
Stack Trace:
[System.InvalidOperationException: Enumeration already finished.]
来自 GetEnumerator()
的输出。这不会抛出,但在这种情况下会 return default(T)
, null
。
Cursor moved: True
Current element is null: False
Current element: 1
Cursor moved: False
Current element is null: True
Current element:
来自 GetEnumeratorInt()
的输出。 Enumerable.Range()
returns 一个枚举器,它使用 Current
的最后一个值:
Cursor moved: True
Current element is null: False
Current element: 1
Cursor moved: False
Current element is null: False
Current element: 1
使用 GetEnumeratorIntRangeSelect()
,我们发现 Select()
为我们提供了一个枚举器,其 Current
returns default(T)
在 MoveNext()
[=92 之后=] 是错误的。这对于上面的 GetEnumerator()
来说确实是多余的,但它说明了值类型而不是引用类型会发生什么。
Cursor moved: True
Current element is null: False
Current element: 1
Cursor moved: False
Current element is null: False
Current element: 0
我们通过调用 ToList()
获得的 List<T>
枚举器也在结束后从 Current
returns default(T)
获得。
我没有找到在结束后引发 Current
的通用 IEnumerator<T>
,但如您所见,我并没有找到一个很好的项目。
1 或者类似的话;我找不到确切的措辞。
2 可能不是真正的计算机科学家。