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

通用类的类帮助器?

  •  8
  • jpfollenius Rob Kennedy  · 技术社区  · 15 年前

    我用的是Delphi2009。是否可以为通用类(即TQueue)编写类助手。明显的

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

    不起作用,也不起作用

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

    正如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;
    

    就像一个“正式”的助手一样,你永远不会例示这个 拖鞋 类,您只使用它来改变 TFO 类,除非您必须是显式的。在您的代码中,当您需要使用 TFO 使用你的“助手”方法,你必须硬铸:

       TFooHelper(someFoo).MyHelperMethod;
    

    缺点:

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

    2. 必须显式强制转换才能使用助手

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

    优势:

    1. 如果您开始使用其他一些“帮助”同一个基类的代码,那么您的助手将不会破坏任何风险。

    2. 显式类型转换在您的“使用者”代码中清楚地表明,您使用的类的方式不直接受类本身的支持,而不是在某些语法甜言蜜语后面敷衍和隐藏这一事实。

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

        2
  •  12
  •   Kenneth Cochran    12 年前

    我现在仍然使用Delphi2009,所以我想我可以添加一些其他方法来扩展一个通用类。在较新版本的Delphi中,这些应用程序应该同样适用。让我们看看添加 ToArray 方法到列表类。

    拦截器

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

    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。不过,您可能会遇到第三方库的问题。

    这个 装饰者 模式

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

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

    缺点

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

    边注

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

        3
  •  7
  •   Mason Wheeler    15 年前

    据我所知,没有办法将类助手放在通用类上并让它编译。你应该把这作为一个错误向质量控制报告。