泛级类的助手?

jpf*_*ius 8 delphi generics delphi-2009 class-helpers

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

TQueueHelper <T> = class helper of TQueue <T>
  ...
end;
Run Code Online (Sandbox Code Playgroud)

不起作用,也不起作用

TQueueHelper = class helper of TQueue
  ...
end;
Run Code Online (Sandbox Code Playgroud)

Del*_*ics 13

正如Delphi帮助中所记录的那样,类助手不是为通用目的而设计的,并且它们被错误地认为具有许多限制甚至是错误.

然而,在我看来,有一种观念 - 不正确和危险 - 这些是通用"工具包"中的合法工具.我在博客上写了为什么这是错误的,随后关于你如何通过遵循社会责任编码模式来减轻危险(尽管这不是防弹).

通过对从您尝试扩展的类派生的"伪"类进行硬强制转换,您可以实现类帮助程序的效果,而不会出现任何这些错误或限制或(最重要的)风险.即代替:

TFooHelper = class helper for TFoo
  procedure MyHelperMethod;
end;
Run Code Online (Sandbox Code Playgroud)

使用

TFooHelper = class(TFoo)
  procedure MyHelperMethod;
end;
Run Code Online (Sandbox Code Playgroud)

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

   TFooHelper(someFoo).MyHelperMethod;
Run Code Online (Sandbox Code Playgroud)

缺点:

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

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

  3. 如果使用辅助揭露保护成员必须(除非您公开暴露所需的保护成员的公共方法)宣布在你使用它在同一单元的帮手

好处:

  1. 如果您开始使用其他"帮助"相同基类的代码,那么您的帮助程序绝对没有风险

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

它并不像班级助手一样"干净",但在这种情况下,"清洁"的方法实际上只是把地毯弄得乱七八糟,如果有人打扰地毯,你最终会陷入比你开始时更大的混乱.

  • 对我而言,"可读"同样多 - 如果不是*更多* - 关于能够清楚地看到正在发生的事情,因为它是关于减少打字的数量.当我看到使用某个类"TxxxHelper"应用硬强制转换时,我知道有一些猴子业务正在进行中.猴子生意我绝对可能需要注意,如果不是现在,那么在将来我可能忘记了"MyMethod"不是它看起来的类的一部分,也无法弄清楚为什么其他一些代码尝试使用它将无法编译(即我没有在范围内的正确帮助或被另一个"模糊"). (2认同)

Ken*_*ran 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;
Run Code Online (Sandbox Code Playgroud)

请注意,您需要使用完全限定名称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;
Run Code Online (Sandbox Code Playgroud)

再一次有优点和缺点.

好处

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

缺点

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

边注

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


Mas*_*ler 7

就像我所知道的那样,没有办法将类助手放在泛型类上并进行编译.您应该将此报告给QC作为错误.

  • 无论是谁投了这个答案:你能解释一下原因吗? (4认同)
  • 这不是我,但我想这是因为如果它是设计,那么它不是一个错误.正如类助手实现与Delphi.NET等价物相比最初是"不完整的".有些人认为这是一个错误,但CodeGear的官方回应是"按设计" - 类帮助者不支持他们不打算用于的东西的事实不是"一个bug".要求他们做的比他们目前设计的更多是*增强请求*.:) (4认同)