代码之家  ›  专栏  ›  技术社区  ›  jpfollenius Rob Kennedy

通用类的类助手?

  •  11
  • jpfollenius Rob Kennedy  · 技术社区  · 16 年前

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

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

    不起作用,也不起作用

    TQueueHelper = class helper of TQueue
      ...
    end;
    
    3 回复  |  直到 16 年前
        1
  •  11
  •   Deltics    16 年前

    我目前仍在使用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。
    • 您可以创建额外的装饰器来添加更多功能,并以不同的方式组合装饰器。您甚至可以使用它来模拟多重继承,尽管接口仍然更容易。

    缺点

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

    边注

    所以,如果你想让一个类几乎不可能扩展,那就把它变成一个密封的泛型类。然后类助手不能触摸它,也不能从中继承。剩下的唯一选择就是包装它。

        2
  •  12
  •   Kenneth Cochran    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;
    

    就像使用“正式”助手一样,你永远不会实例化这个 类,你只用它来改变 TFoo 类,除非在这种情况下你必须明确。在代码中,当你需要使用某个实例时 TFoo 使用你的“助手”方法,你必须硬铸:

       TFooHelper(someFoo).MyHelperMethod;
    

    缺点:

    1. 你必须遵守适用于助手的相同规则-没有成员数据等(除了编译器不会“提醒你”之外,这根本不是一个缺点)。

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

    优势:

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

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

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

        3
  •  7
  •   Mason Wheeler    16 年前

    据我所知,没有办法将类助手放在泛型类上并编译它。你应该把这当作bug报告给QC。

    推荐文章