delphienumsrttitypeinfo

Why do I get "type has no typeinfo" error with an enum type


I have declared the following enum type in which I want the first member to have the ordinal value of 1 (one) rather than the usual 0 (zero):

  type
    TMyEnum = (
               meFirstValue = 1,
               meSecondValue,
               meThirdValue
              );

If I call TypeInfo(), e.g. as part of a call to GetEnumName(), I get a compiler error:

  GetEnumName(TypeInfo(TMyEnum), Ord(aValue));

ERROR: "E2134: Type 'TMyEnum' has no typeinfo"

Why is this?

I know that classes only have typeinfo if they are compiled with the $M compiler option enabled or (derive from some class which was, such as TPersistent) but I didn't think there were any special conditions for having typeinfo for enum types.


Solution

  • Type information is not supported for enums where specific ordinal values are assigned that result in enum members having ordinal values that are different to those that would normally be assigned by the compiler.

    If specific values are essential or desirable, "unused" enum members will have to be inserted to "pad" the enum as required. e.g (additional indentation for emphasis only):

      type
        TMyEnum = (
                    meNOTUSED1,   {= 0}
                   meFirstValue,  {= 1} 
                   meSecondValue,
                   meThirdValue
                  );
    

    A subrange can then be used to "filter" out the unused initial value:

       TValidMyEnum = meFirstValue..meThirdValue;
    

    Although you might then wish to consider renaming the original enum type so that your subrange type may be used throughout your project.

    A subrange isn't sufficient if the enum contains "gaps":

      type
        TMyEnum = (
                    meNOTUSED1,   {= 0}
                   meFirstValue,  {= 1} 
                   meSecondValue,
                   meThirdValue,
                    meNOTUSED2,
                   meFinalValue   {= 5}
                  );
    

    In this case there is no simply way to extend compile-time range checking to exclude the unused members, but a couple of set types will simplify the business of implementing any necessary runtime checks:

      type
        TMyEnums = set of TMyEnum;
    
      const
        meNOTUSED      = [meUNUSED1, meUNUSED2]; //  .. etc as required
        meValidValues  = [Low(TMyEnum)..High(TMyEnum)] - meNOTUSED;
    
    
      if NOT (aValue in meValidValues) then
         // etc