Jump to content
Dmitry Onoshko

Dynamic array field of base type declared in descendants

Recommended Posts

What I would like to achieve is along the lines of:

type
  TAncestor = class
    FData: TArray<???>;
    // ...
    // A lot of methods that load, export, change FData
    // in a way common to all the descendants (mostly Length
    // and SetLength are used)
    //
    // Differences in managing FData are extracted into
    // protected virtual methods
    // ...
  end;
  TAncestorClass = class of TAncestor;

  TDescendant1 = class(TAncestor)
  type
    TItem = ... // Some type
    // ...
    // Virtual methods that do descendant-specific things
    // are overriden here
    // ...
  end;

  TDescendant2 = class(TAncestor)
  type
    TItem = ... // Another type
    // ...
    // Virtual methods that do descendant-specific things
    // are overriden here
    // ...
  end;

  ...

The TItem-specific code is all in descendants, TAncestor just implements general management of how and when to resize FData and provides boilerplate code for loading, exporting and performing container-level changes on the data thatwould otherwise be duplicated in every descendant.

 

This could probably be achieved with generics (descending from TAncestor<T> with particular T for each descendant), but this approach fails to support metaclasses and nested types. Am I missing something or is this not possible?

Share this post


Link to post
1 hour ago, Dmitry Onoshko said:

This could probably be achieved with generics (descending from TAncestor<T> with particular T for each descendant), but this approach fails to support metaclasses and nested types. Am I missing something or is this not possible?

Why not simply define a base class for the items to derive from? Then you can have an array of base items, and the derived classes can cast the items as needed.

type
  TItemBase = class
  end;

  TAncestor = class
    FData: TArray<TItemBase>;
    // ...
  end;
  TAncestorClass = class of TAncestor;

  TDescendant1 = class(TAncestor)
  type
    TItem = class(TItemBase)
      //...
    end;
    // ...
    // cast FData elements to TItem as needed...
  end;

  TDescendant2 = class(TAncestor)
  type
    TItem = class(TItemBase)
      //...
    end;
    // ...
    // cast FData elements to TItem as needed...
  end;

  ...

 

Share this post


Link to post
3 minutes ago, Remy Lebeau said:

Why not simply define a base class for the items to derive from? Then you can have an array of base items, and the derived classes can cast the items as needed.

This would turn the whole “dataset” into an array of pointers to a lot of dynamically-allocated pieces. Not quite cache-friendly, the overhead of allocations, etc. The reason to use the dynamic array in the first place was to store data in a single block of memory having all the benefits of good old PODs.

Share this post


Link to post

Meanwhile one thing came to my mind. Along the lines of:

type
  TAncestor = class
    // All the stuff goes here, but no FData definition
    // Pieces of code that use FData directly are
    // virtual abstract methods here
  end;
  TAncestorClass = class of TAncestor;
  
  TAncestor<T> = class(TAncestor)
    FData: TArray<T>;
    // Override FData-related methods common to all classes
    // in the hierarchy here
  end;
  
  TDescendant1Item = ...
  TDescendant1 = class(TAncestor<TDescendant1Item>)
    // ...
  end;
  
  TDescendant2Item = ...
  TDescendant2 = class(TAncestor<TDescendant2Item>)
    // ...
  end;

Lets remove duplicate code from descendants but adds a lot of TXxxxItem type identifiers to the global namespace. Having a way to hide them would be great as the types are for particular class’ internal implementation only.

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×