Class Helper for generic class?

后端 未结 3 972
青春惊慌失措
青春惊慌失措 2021-02-04 12:31

I\'m using Delphi 2009. Is it possible to write a class helper for a generic class, i.e. for TQueue . The obvious

TQueueHelper  = class helper of TQueue         


        
3条回答
  •  半阙折子戏
    2021-02-04 13:08

    I currently still use Delphi 2009 so I thought I'd add a few other ways to extend a generic class. These should work equally well in newer versions of Delphi. Let's see what it would look like to add a ToArray method to a List class.

    Interceptor Classes

    Interceptor classes are classes that are given the same name as the class they inherit from:

    TList = class(Generics.Collections.TList)
    public
      type
        TDynArray = array of T;
      function ToArray: TDynArray;
    end;
    
    function TList.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;
    

    Notice you need to use the fully qualified name, Generics.Collections.TList as the ancestor. Otherwise you'll get E2086 Type '%s' is not completely defined.

    The advantage of this technique is that your extensions are mostly transparent. You can use instances of the new TList anywhere the original was used.

    There are two disadvantages to this technique:

    • It can cause confusion for other developers if they aren't aware that you've redefined a familiar class.
    • It can't be used on a sealed class.

    The confusion can be mitigated by careful unit naming and avoiding use of the "original" class in the same place as your interceptor class. Sealed classes aren't much of a problem in the rtl/vcl classes supplied by Embarcadero. I only found two sealed classed in the entire source tree: TGCHandleList(only used in the now defunct Delphi.NET) and TCharacter. You may run into issues with third party libraries though.

    The Decorator Pattern

    The decorator pattern lets you extend a class dynamically by wrapping it with another class that inherits its public interface:

    TArrayDecorator = class abstract(TList)
    public
      type
        TDynArray = array of T;
      function ToArray: TDynArray; virtual; abstract;
    end;
    
    TArrayList = class(TArrayDecorator)
    private
      FList: TList;
    public
      constructor Create(List: TList);
      function ToArray: TListDecorator.TDynArray; override;
    end;
    
    function TMyList.ToArray: TListDecorator.TDynArray;
    var
      I: Integer;
    begin
      SetLength(Result, self.Count);
      for I := 0 to Self.Count - 1 do
      begin
        Result[I] := FList[I];
      end;
    end;
    

    Once again there are advantages and disadvantages.

    Advantages

    • You can defer introducing the new functionally until its actually needed. Need to dump a list to an array? Construct a new TArrayList passing any TList or a descendant as a parameter in the constructor. When you're done just discard the TArrayList.
    • You can create additional decorators that add more functionality and combine decorators in different ways. You can even use it to simulate multiple inheritance, though interfaces are still easier.

    Disadvantages

    • It's a little more complex to understand.
    • Applying multiple decorators to an object can result in verbose constructor chains.
    • As with interceptors you can't extend a sealed class.

    Side Note

    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.

提交回复
热议问题