Do you know of any examples which are less UI oriented?
There are two man use cases for RTTI. First the most obvious one is data serialization and deserialization. For example, if you want to take a record and serialize it into json, right now you have to do it like this:
jsonObject.Strings['MyField1'] := MyRec.MyField1;
jsonObject.Integers['MyField2'] := MyRec.MyField2;
jsonObject.Arrays['MyArrayField'] := SerializeArray(MyRec.MyArrayField);
Extremely annoying and error prone. With Extended RTTI, where field names are written into the RTTI, you could write a serializer that will just take all public fields and writes them down, recursively descending into other records or arrays. Then it would just be:
SerializeRecord(@MyRec, TypeInfo(TMyRec));
And when you update your record, rename fields, etc. the serializer will still work because of RTTI.
This is especially useful combined with the custom attributes, where you could do things like that:
type
TMyRecord = record
[SerializeName('Field1'), SerializeOptional('Default')]
FField1: String;
[SkipSerialize]
FHiddenField: Integer;
//...
so SerializeName would be an attribute to have a custom name, e.g. for backwards compatibility, or to not have the leading F as in this case. SerializeOptional could be an attribute to say when deserializing and this object is not there, fill it with 'Default'. SkipSerialize would tell the serializer that FHiddenField is an internal field not to be serialized into json.
This way you do not need to write a single line of (error prone) code to convert your data into other formats, as all the required information can be encoded into the definition of the type.
The second main use case is reflection. Basically altering the behavior of your code at runtime. For example you could have a module system where you scan your filesystem for a module (e.g. in form of a dynamic library) and if a new module is found the module is loaded and RTTI is used to e.g. load these function pointers into existing objects/classes at runtime without having to restart the application.
For example you could create a software where classes objects have logger fields, and on startup, the module loader would look if there is a logger module available, and if so, scan the classes, if any has a logger field, and dynamically load the logger into that field.