泛级类的助手?

时间:2009-10-21 11:27:54

标签: delphi generics delphi-2009 class-helpers

我正在使用Delphi 2009.是否可以为泛型类编写一个类助手,即用于TQueue。显而易见的

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

不起作用,也不起作用

TQueueHelper = class helper of TQueue
  ...
end;

3 个答案:

答案 0 :(得分:13)

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

然而,在我看来,有一种观念 - 不正确和危险 - 这些是通用“工具包”中的合法工具。我在博客上发表了关于why this is wrong的博客,随后又谈到了如何通过某种方式来减轻following a socially responsible coding pattern带来的危险(尽管这不是防弹)。

通过使用强制转换为“伪”,您可以在不使用任何这些错误或限制或(最重要的)风险的情况下实现类帮助程序的效果从您尝试扩展的类派生的类。而不是:

TFooHelper = class helper for TFoo
  procedure MyHelperMethod;
end;

使用

TFooHelper = class(TFoo)
  procedure MyHelperMethod;
end;

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

   TFooHelper(someFoo).MyHelperMethod;

缺点:

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

  2. 您必须明确转换才能使用您的助手

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

  4. 优点:

    1. 如果您开始使用“帮助”相同基类的其他代码,那么您的帮助程序将会中断的风险绝对没风险

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

    3. 它并不像班级助手一样“干净”,但在这种情况下,“清洁”的方法实际上只是将地毯扫到地毯下,如果有人打扰地毯,你最终会陷入比你开始时更大的混乱。

答案 1 :(得分:12)

我目前仍在使用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的实例。

这种技术有两个缺点:

  • 如果他们不知道您重新定义了熟悉的课程,可能会给其他开发人员造成混淆。
  • 不能用于密封类。

通过仔细的单元命名并避免在与拦截器类相同的位置使用“原始”类,可​​以减轻混淆。在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。
  • 您可以创建其他装饰器,以添加更多功能并以不同方式组合装饰器。您甚至可以使用它来模拟多重继承,但接口仍然更容易。

缺点

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

旁注

所以看起来如果你想让一个类几乎不可能扩展,那就让它成为一个密封的泛型类。然后类帮助程序无法触及它,也无法继承它。关于唯一的选择是包装它。

答案 2 :(得分:7)

尽可能地说,没有办法将类助手放在泛型类上并让它编译。您应该将此报告给QC作为错误。