11

我正在使用 Delphi 2009。是否可以为泛型类(即 TQueue )编写类助手。显而易见的

TQueueHelper <T> = class helper of TQueue <T>
  ...
end;

不工作,也不

TQueueHelper = class helper of TQueue
  ...
end;
4

3 回答 3

13

正如 Delphi 帮助中所述,类助手不是为通用用途而设计的,因此它们被错误地认为具有许多限制甚至错误。

然而,有一种看法——在我看来是不正确和危险的——认为这些是通用“工具包”中的合法工具。我在博客上写过为什么这是错误的,然后是关于如何通过遵循对社会负责的编码模式来减轻危险的方法(尽管这也不是防弹的)。

通过对从您尝试扩展的类派生的“伪”类使用硬强制转换,您可以在没有任何这些错误或限制或(最重要的)风险的情况下实现类助手的大部分效果。即代替:

TFooHelper = class helper for TFoo
  procedure MyHelperMethod;
end;

采用

TFooHelper = class(TFoo)
  procedure MyHelperMethod;
end;

就像使用“正式”助手一样,您永远不会实例化这个TFooHelper类,您只使用它来改变TFoo类,除非在这种情况下您必须是显式的。在您的代码中,当您需要使用“帮助器”方法使用某个TFoo实例时,您必须进行硬转换:

   TFooHelper(someFoo).MyHelperMethod;

缺点:

  1. 您必须遵守适用于帮助程序的相同规则 - 没有成员数据等(根本不是真正的缺点,除了编译器不会“提醒您”)。

  2. 你必须明确地使用你的助手

  3. 如果使用助手来公开受保护成员,则必须在使用它的同一单元中声明助手(除非您公开公开所需的受保护成员的公共方法)

好处:

  1. 如果您开始使用“帮助”同一基类的其他代码,绝对没有您的助手会中断的风险

  2. 显式类型转换在您的“消费者”代码中清楚地表明您正在以类本身不直接支持的方式使用该类,而不是在一些语法糖后面捏造和隐藏该事实。

它不像班级助手那样“干净”,但在这种情况下,“更干净”的方法实际上只是扫除地毯下的烂摊子,如果有人打扰了地毯,你最终会得到比开始时更大的烂摊子。

于 2009-10-21T18:33:55.380 回答
13

我目前仍在使用 Delphi 2009,所以我想我会添加一些其他方法来扩展泛型类。这些应该在较新版本的 Delphi 中同样有效。ToArray让我们看看将方法添加到 List 类会是什么样子。

拦截器

拦截器类是与它们继承的类同名的类:

TList<T> = class(Generics.Collections.TList<T>)
public
  type
    TDynArray = array of T;
  function ToArray: TDynArray;
end;

function TList<T>.ToArray: TDynArray;
var
  I: Integer;
begin
  SetLength(Result, self.Count);
  for I := 0 to Self.Count - 1 do
  begin
    Result[I] := Self[I];
  end;
end;

请注意,您需要使用完全限定名称Generics.Collections.TList<T>作为祖先。否则你会得到E2086 Type '%s' is not completely defined.

这种技术的优点是您的扩展大多是透明的。您可以在使用原始 TList 的任何地方使用新 TList 的实例。

这种技术有两个缺点:

  • 如果其他开发人员不知道您重新定义了熟悉的类,他们可能会感到困惑。
  • 它不能用于密封类。

通过仔细的单元命名和避免在与拦截器类相同的位置使用“原始”类可以减轻混淆。在 Embarcadero 提供的 rtl/vcl 类中,密封类不是什么大问题。我只在整个源代码树中发现了两个密封类:TGCHandleList(仅在现已失效的 Delphi.NET 中使用)和 TCharacter。不过,您可能会遇到第三方库的问题。

装饰器模式

装饰器模式允许您通过用另一个继承其公共接口的类包装一个类来动态扩展一个类:

TArrayDecorator<T> = class abstract(TList<T>)
public
  type
    TDynArray = array of T;
  function ToArray: TDynArray; virtual; abstract;
end;

TArrayList<T> = class(TArrayDecorator<T>)
private
  FList: TList<T>;
public
  constructor Create(List: TList<T>);
  function ToArray: TListDecorator<T>.TDynArray; override;
end;

function TMyList<T>.ToArray: TListDecorator<T>.TDynArray;
var
  I: Integer;
begin
  SetLength(Result, self.Count);
  for I := 0 to Self.Count - 1 do
  begin
    Result[I] := FList[I];
  end;
end;

再次有优点和缺点。

好处

  • 您可以推迟引入新功能,直到实际需要。需要将列表转储到数组中吗?构造一个新的 TArrayList,将任何 TList 或后代作为构造函数中的参数传递。完成后,只需丢弃 TArrayList。
  • 您可以创建额外的装饰器来添加更多功能并以不同的方式组合装饰器。您甚至可以使用它来模拟多重继承,尽管接口仍然更容易。

缺点

  • 理解起来有点复杂。
  • 将多个装饰器应用于对象可能会导致冗长的构造函数链。
  • 与拦截器一样,您不能扩展密封类。

边注

So it seems that if you want to make a class nearly impossible to extend make it a sealed generic class. Then class helpers can't touch it and it can't be inherited from. About the only option left is wrapping it.

于 2011-09-15T20:55:07.000 回答
8

据我所知,没有办法将类助手放在泛型类上并对其进行编译。您应该将其作为错误报告给 QC。

于 2009-10-21T11:35:06.150 回答