Nested Type Enumerations

Over the last couple of years, I have been making more use of nested types to organise how my code (behaviour) is organised in an application.  If some application behaviour (encapsulated in class A) is part of a broader behaviour (encapsulated in class B), then I will at least consider nesting class A within class B.

For example, if I were writing a (very simple) word processor and had a class for Document and a class for Paragraph, then I could declare these classes as:

class = TDocument
public
  type
    class = TParagraph
      ...
    end;
  ...
end;

Organising classes like this makes it easier to understand relationships between related behaviours in a program. It also makes it somewhat easier to find where particular behaviours are declared in the program as there is more organisation to classes.

The obvious downside is that class/type declarations are larger and harder to read.  With practice, I have gotten better at reading types with nested types but you can only go so far.

One area in which nested types works well, is in the declaration of enumerated types.  Often you want some behaviour associated with enumerated types.  For example, you may want to associate display names with them or what string value to use when writing out to XML.  Consider this example of an enumerated type which identifies the importance of a document:

TDocumentImportance = (diLow, diMedium, diHigh);

Nowadays, I would probably declare such an enumeration with the following type:

TDocumentImportance = record
public
  type
    TId = (diLow, diMedium, diHigh);
private
  type
    TInfo = record
      Id: TId;
      Name: string;
      XmlValue: string;
    end;
    TInfos = array[TId] of TInfo;
  const
    Infos: TInfos =
    (
      (Id: diLow; Name: 'Low'; XmlValue: 'Low'),
      (Id: diMedium; Name: 'Medium'; XmlValue: 'Medium'),
      (Id: diHigh; Name: 'High'; XmlValue: 'High')
    );
  class constructor Create;
public
  class function IdToName(Value: TId): string; static;
  class function IdToXmlValue(Value: TId): string; static;
  class function TryXmlValueToId(const XmlValue: string; out Id: TId): Boolean; static;
end;

Whenever I now want to define one of these enumerations, I use the type:

TDocumentImportance.TId;

The functions (behaviour) associated with Document Importance are now part of the TDocumentImportance record instead of being global functions. (I normally also include a class constructor to check that the order of the Info records matches the order of the TId enumeration to allow easy IdToXXX conversions.)

While declaring enumerated types this way may be a bit more work, I find its benefits in improving my “mental map” of the source code, justify the extra effort.

Posted in Coding Techniques Tagged with: ,
4 comments on “Nested Type Enumerations
  1. I think you mean enumerations and not enumerators.

  2. I use this extensively for test cases, to define input and expected output. Unfortunately, the IDE seems to get confused with a lot of local or nested type declarations and const definitions. At some point it just keeps complaining about undefined identifiers and such… and fails to give code completion support. :-( However, everything compiles nicely…

  3. fabvit says:

    Can you please show a snippet of code on how to use it? I’ve tried but could not grasp how to do :-(
    thank you, fabio

1 Pings/Trackbacks for "Nested Type Enumerations"
  1. […] notional) “Spirit of Delphi”. It’s time for another one, this time inspired by a post by Paul Klink of the ADUG. In his post, Paul makes the following observation: The obvious downside is that class/type […]

2015 Symposium

Melbourne - March 26

Canberra - March 27

Registrations have now closed.

The ADUG wishes to acknowledge the assistance of the following companies whose sponsorship helps to make our Symposium possible:

Arena Business Technology TCG-Logo Nexus DB Help and Manual Devart Gnostice Information Technologies

Full details on the Symposium page

Archives