Delphi has an enumerated type since the beginning.

I'm sure some of us have from time to time, wonder how to extend an enumerated type, like this developer here.

With record helpers, you can now extend it.

Given this declaration,

  TRegDataType = (rdUnknown, rdString, rdExpandString, rdInteger, rdBinary);

Here's how you can extend it. You can use both Succ, Ord on an existing member, or the type itself to typecast a number.

type
  TRegDataTypeHelper = record helper for TRegDataType
  const
    rdNewMember1 = Succ(rdBinary);
    rdNewMember2 = Succ(rdNewMember1);
    rdNewMember3 = TRegDataType(Ord(rdNewMember2) + 1); 
    rdNewMember4 = TRegDataType(10);  // explicitly typecast a value
  end;

And after you've done this, you can now use it normally:

 var LRegDataType: TRegDataType;
 LRegDataType := TRegDataType.rdNewMember1;

Note, if you tried this...

 LRegDataType := rdNewMember1;

It wouldn't work, since rdNewMember1 is not in the global scope.

What can you do? Well, you can bring it into the global scope by declaring it as a const.

const
  rdNewMember1 = TRegDataType.rdNewMember1; 
  rdNewMember2 = TRegDataType.rdNewMember2;
  rdNewMember3 = TRegDataType.rdNewMember3;
  rdNewMember4 = TRegDataType.rdNewMember4;

Even though you've declared it as a const, note that the newly declared const is actually an enum value.

And now, you can access it without the scope.

The compiler is unaware that you've added new members to the enumerated type, so if you tried High(TRegDataType), you'll get the value of rdBinary, and not rdNewMember2.

So, you can declare a const, High, that returns the last value.

type
  TRegDataTypeHelper = record helper for TRegDataType
  const
    rdNewMember1 = Succ(rdBinary);
    rdNewMember2 = Succ(rdNewMember1);
    High         = rdNewMember2;
  end;

And then to use it, you just say:

  TRegDataType.High

instead of

  High(TRegDataType);

If you use the new members in a case expression like so:

  case Value of
    rdBinary:        Result := REG_BINARY;
    rdExpandString:  Result := REG_EXPAND_SZ;
    rdInteger:       Result := REG_DWORD;
    rdString:        Result := REG_SZ;
    rdNewMember1:    ; // do something here
  end;

the Delphi compiler will warn W1018 Case label outside of range of case expression, however, it will still compare it.