规范枚举器MoveNext函数中是否需要if语句?

时间:2012-10-20 17:32:38

标签: delphi

Delphi枚举器中的规范MoveNext如下所示:

function TListEnumerator.MoveNext: Boolean;
begin
  Result := FIndex < FList.Count - 1;
  if Result then
    Inc(FIndex);
end;

这是整个RTL,VCL等中使用的形式。这种形式似乎也广泛存在于3 rd 聚会代码中。

我认为它可以更简单地写成:

function TListEnumerator.MoveNext: Boolean;
begin
  Inc(FIndex);
  Result := FIndex < FList.Count;
end;

有什么理由不能使用更简单的表格吗?


我的推理如下。 MoveNext返回False后,永远不会再访问Current属性。 FIndex不在列表的末尾并不重要,因为它永远不会再次使用。 for in循环实际上是这样实现的:

while Enumerator.MoveNext do
  Enumerator.Current.DoSomething;

事实上,FIndex越界越有意义。这意味着如果有人使用手写的枚举器代码,那么在Current返回MoveNext后访问False时,他们将获得范围检查错误。

在第一次调用FIndex之前,-1MoveNext。这是左边列表中的一个。最后一次调用MoveNext后,返回False的{​​{1}}不适合FIndex Count,这是右边列表中的一个。

3 个答案:

答案 0 :(得分:3)

您假设TListEnumerator只会在for循环中使用。虽然这是迄今为止最常见的情况,但如果不是这样,您建议的版本可能会出错。在MoveNext已经返回False之后再次调用MoveNext就可以了。如果你继续调用True直到它返回FIndex = MaxInt,你必须得到一个无限循环,并且使用你建议的版本,循环不会是无限的,它会在你超过{时终止{1}}案例。

请参阅Delphi实现所基于的IEnumerator.MoveNext文档(IIRC,它实际上首先在现已死的Delphi .NET中提供):

  

当枚举数位于此位置时,后续对MoveNext的调用也会返回false,直到调用Reset。

答案 1 :(得分:2)

纯粹在语义上思考,我会说原始版本是正确的。把它想象成一个对话:

Application: Hey, list. Go to the next item.
List: No, sorry. I can't.
Application: Oh, allright.
  <some time later:>
Application: List, please give me the current item
List: No problem, here you go.


Whereas in your suggestion, it would go like this:


Application, Hey, list. Go to the next item.
List: No, that's impossible.
Application: Oh, all right.
  <some time later:>
Application: Hi again, List. Give me the current item.
List: Here you go. 
Application: Thanks, mate.
  <some time later, when using the item:>
Application: What the ????!

答案 2 :(得分:1)

有趣的问题(+1)。如果不了解普查员的所有可能用途(或者根本不使用它),乍一看,它的功能在列表的开头和结尾之间是不同的,这看起来确实很奇怪。如你所说:Current在开始时会导致列表索引超出范围错误,那么为什么不在最后呢?似乎不一致。

但是进一步思考它:一旦创建了一个枚举器,无论是什么或者任何人,列表的边界都是未知的,所以第一个元素不是也不能被选中。这与边界已知并且索引限于。

时的行为相比较

数据集的功能完全相同,但枚举器的实现可能会受益于BOF - 和EOF一样的行为。