Overview
This blog post looks at Enums and how multi-platform generics and RTTI (or what C# guys call reflection) make this really simple using common code on Windows, Mac OS X, iOS and Android using Delphi.
Starting with what an Enum is, the post then explores how you would traditionally have seen it written in code, before looking at the clean X-platform approach RTTI and Generics provide.
If your experienced in working with enums and want to find out how RTTI makes it easier, you may want to skip the first part of this post and get to the section about RTTI further down.
Enumerated Types to and from strings
An Enum, or Enumerated Type is a data type with a set of named values.
Enums are a great way to define a set of constants that are specific for a type and work with them in context, ensuring only one, or an array of allowed values is set.
OK, I’m going to use an example that maybe should have more values listed, but you get the point. It is the points of a compass – North, South, East or West.
type TCompass = (North, South, East, West); var D : TCompass; // short version for Direction begin D := TCompass.North; case D of North : ShowMessage('North'); South : ShowMessage('South'); East : ShowMessage('East'); West : ShowMessage('West'); raise Exception.Create('Unknown Direction'); end; end;
This provides one way to convert a Enum to a string using a case statement, but every time you make a change to your Enum types, you need to add in extra code… Call me lazy, but thats too much work!
Enum to string – GetEnumName
In Delphi it has been possible for a long time to convert an Enum to a string. This has traditionally been done using the GetEnumName method in System.typInfo.
function GetEnumName(TypeInfo: PTypeInfo; Value: Integer): string;
Rewriting the previous example using GetEnumName makes code simpler and more robust.
type
TCompass = (North, South, East, West);
var
D : TCompass; // D = Direction
S : string;
begin
D := TCompass.North;
S := GetENumName(TypeInfo(TCompass),D);
ShowMessage(S);
end;
This is great as I no longer need to worry about changes to the Enum declaration, my code will keep working.
String to Enum – Using GetENumValue
To convert a string to an enum, you can use the GetEnumValue method in System.Typinfo – this is a little more work than going the other way however.
function GetEnumValue(TypeInfo: PTypeInfo; const Name: string): Integer;
GetEnumValue will return an integer that you can then typecast back to the value in the enum. Assuming we have a variable called value of type Integer and we use the S variable from earlier
Value := GetEnumValue(TypeInfo(TCompass),S)); D := TCompass(Value);
Presto! Easy to convert values back and forth. However its not the easiest code to read quickly, but this does work cross platform on all the delphi compilers
Enum conversion with Generics / RTTI
Unit System.RTTI is cross platform and contains a great class for converting enum to string and back: TRttiEnumerationType
The TRttiEnumerationType class has two class functions (methods you can call without creating an instance of the class) that clean up the code required from using the TypInfo methods. The easy reading version of these method declarations is:
class function GetName<T>(AValue: T):string; class function GetValue<T>(AName: string): T;
Note these methods use Generics (thats the T bit). Generics are very cool as they allow you to write functionality once and then reuse it with different types at different times in code.
In this instance TRttiEnumerationType’s generic methods are for use with Enums only and not other class types as the functionality defined is specific for Enum’s.
To convert the TCompass enum now after adding RTTI to the uses would look like this.
S := TRttiEnumerationType.GetName<TCompass>(D); ShowMessage(S);
To convert back from a string is also simpler.
D := TRttiEnumerationType.GetValue<TCompass>(S);
How much easier is that to read! and as we have only had to declare the type once, we have less chance of silly copy paste errors in code.
Summary
Thanks to cross platform unit System.RTTI and its class TRttiEnumerationType (which also uses multi-platform Generics), it is really simple to write really readable code that works with enums and compiles for Windows, Mac OS X, iOS and Android.
Hi,
Yes, the System.Rtti way is nicer code wise…
Any difference in speed that you are aware between these two methods ?
Paul
Should be the same. I’ve not tried speed testing it as it calls the same code under the hood (but in a slightly more complete way). Open System.RTTI and have a look 🙂
Good to know, thank you for this nice trick.
Even this is possible (no generic needed if type is known):
TRttiEnumerationType.GetName(D);
I even have made a static class “Enums” for this:
class function Enums.ToString(const aValue: T): string;
begin
Result := TValue.From(aValue).ToString;
end;
MyString := Enums.ToString(D);