为什么TEnumerable<T>使用传递方法?

13

TEnumerable<T>是所有泛型集合类的基类,其声明非常奇怪,如下所示:

type
  TEnumerable<T> = class abstract
  protected
    function DoGetEnumerator: TEnumerator<T>; virtual; abstract;
  public
    function GetEnumerator: TEnumerator<T>;
  end;

function TEnumerable<T>.GetEnumerator: TEnumerator<T>;
begin
  Result := DoGetEnumerator;
end;

TEnumerator<T> 声明了一个公共的 MoveNext 方法和一个私有的 DoMoveNext 函数,而 MoveNext 仅仅是调用了 DoMoveNext

除了增加额外的函数调用开销、使调用堆栈更长且让试图从这些基类继承的程序员感到困惑之外,有人能解释一下它的作用吗?如果存在实际优势,请问是什么,因为我没有看到...


5
实际上,它可以减少函数调用开销并缩短调用栈... :) - Barry Kelly
3个回答

31

免责声明:本人编写了TEnumerable<T>。如果我能重新编写,我可能会以更简单的方式来考虑性能问题,因为我意识到这种优化会让很多人感到困惑。

它旨在避免在for-in循环中进行虚拟调用,同时保持与多态性的兼容性。这是一般模式:

  • 基类Base定义了受保护的虚拟抽象方法V和公共非虚拟方法MM分派到V,因此通过Base类型的变量进行的多态调用将路由到V的重载行为。

  • 子类,例如Desc,实现了静态覆盖M(隐藏了Base.M),其中包含实现,并实现了调用Desc.MV的重载。通过Desc-类型的变量调用M将直接进入实现而无需进行虚拟调用。

具体示例:当编译器为这个序列生成代码时:

var
  someCollection: TSomeCollection<TFoo>;
  x: TFoo;
begin
  // ...
  for x in someCollection do
    // ...
end;

编译器会在someCollection的静态类型中查找名为GetEnumerator的方法,并在其返回的类型上查找名为MoveNext的方法(对于Current属性也是如此)。如果该方法具有静态分派,则可以消除虚拟调用。

这对于循环最为重要,因此需要优化MoveNext / Current访问器。但是,为了使优化生效,GetEnumerator方法的返回类型必须具有协变性,即它需要静态返回正确的派生枚举器类型。但是,在Delphi中,与C++[1]不同,无法使用更具派生性的返回类型覆盖祖先方法,因此需要出于不同的原因应用相同的技巧来更改后代的返回类型。

优化还可能允许内联MoveNextGetCurrent方法调用,因为静态编译器很难“看穿”虚拟调用并仍然保持快速。

[1] C++支持对重写方法进行返回值协变。


4
GetEnumerator() 方法不是虚方法,无法重写。这是确保GetEnumerator()始终存在,始终采用固定的参数集(在本例中为零),并且某些程序员不会将其弄乱以供子类使用的一种方式。任何使用TEnumerable或其子类的人都可以调用GetEnumerator()。
但是,由于将有不同的TEnumerable子类执行不同的操作,因此DoGetEnumerator()允许程序员对结构进行内部更改。 "virtual" 允许重写该方法。 "abstract" 强制要求子类实现该方法-编译器不会让你忘记。由于DoGetEnumerator()被声明为protected(至少在此级别上),因此使用TEnumerable子类的程序员无法绕过GetEnumerator()并直接调用DoGetEnumerator()。

3

您无法像重新引入GetEnumerator一样执行技巧:

function TDictionary<TKey, TValue>.TKeyCollection.DoGetEnumerator: TEnumerator<TKey>;
begin
  Result := GetEnumerator;
end;
...
function TDictionary<TKey, TValue>.TKeyCollection.GetEnumerator: TKeyEnumerator;
begin
  Result := TKeyEnumerator.Create(FDictionary);
end;

创建适当的本地/特定TEnumerator


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接