unit EnumHelper; interface {$mode delphi} uses SysUtils, TypInfo; type EEnumOutOfRange = class(EArgumentOutOfRangeException); EEnumNameNotFound = class(Exception); EEnumParseError = class(Exception); {$REGION 'ContainedEnum'} ContainedEnum = record strict private FEnumValue: T; constructor Create(const Value: T); public class operator Implicit(const Value: T): ContainedEnum; inline; class operator Implicit(const Value: Integer): ContainedEnum; class operator Implicit(const Value: string): ContainedEnum; class operator Implicit(const Value: ContainedEnum): string; class operator Implicit(const Value: ContainedEnum): Integer; // class operator Implicit(const Value: ContainedEnum): T; function ToInteger: Integer; function ToString: string; property Value: T read FEnumValue; end; {$ENDREGION} TArray = array of T; EnumArray = array of ContainedEnum; {$REGION 'Enum'} /// Record que contiene metodos estaticos para trabajar con tipos enums Enum = record strict private class function EnumTypeInfo: PTypeInfo; static; class function EnumTypeData: PTypeData; static; class procedure NameNotFound(const Identifier: string; const Value: T); static; class procedure OutOfRange(const Value: T; const Namespace, MethodName: string); static; class procedure StringParseError(const Value: string); static; public /// El nombre del tipo enum class function TypeName: string; static; /// El nombre del valor enum class function ValueName(const Value: T): string; static; /// Devuelve el valor enum dado un Ordinal class function Parse(const Ordinal: Integer): ContainedEnum; overload; static; /// Devuelve el valor enum dado el nombre de su declaracion class function Parse(const EnumValueName: string): ContainedEnum; overload; static; /// Convierte el valor enum a su correspondiente Ordinal class function ToInteger(const Value: T): Integer; static; /// El valor maximo del enum. Equivalente a Ord(High(T)) class function MaxValue: Integer; static; /// El valor maximo del enum. Equivalente a Ord(Low(T)) class function MinValue: Integer; static; /// Devuelve True si el valor del tipo enum se encuentra dentro del rango permitido class function TypeInRange(const Value: T): Boolean; static; /// Devuelve True si el entero se encuentra dentro del rango permitido del tipo enum class function IntegerInRange(const Value: Integer): Boolean; static; /// Eleva una excepcion EEnumOutOfRange si el valor del tipo enum esta fuera del rango // permitido /// El valor a testear /// Describe el "contexto" de quien invoca a este metodo (ej clase o unidad) /// Nombre del metodo que invoco a esta rutina class procedure CheckInRange(const Value: T; const Namespace, MethodName: string); static; /// Cantidad de elementos del enum class function Count: Integer; static; /// Devuelve un Array con los elementos del enum class function AsArray: EnumArray; static; end; {$ENDREGION} implementation uses Types, Math, StrUtils; {$REGION 'Enum'} class function Enum.TypeInRange(const Value: T): Boolean; begin Result := IntegerInRange(ToInteger(Value)); end; class function Enum.IntegerInRange(const Value: Integer): Boolean; begin Result := InRange(Value, MinValue, MaxValue); end; class function Enum.MaxValue: Integer; begin Result := Enum.EnumTypeData.MaxValue; end; class function Enum.MinValue: Integer; begin Result := Enum.EnumTypeData.MinValue; end; class function Enum.ToInteger(const Value: T): Integer; begin Result := 0; System.Move(Value, Result, System.SizeOf(Value)); end; class function Enum.TypeName: string; begin Result := string(Enum.EnumTypeInfo.Name); end; class function Enum.ValueName(const Value: T): string; begin Result := TypInfo.GetEnumName(Enum.EnumTypeInfo, Enum.ToInteger(Value)); end; class function Enum.EnumTypeData: PTypeData; begin Result := TypInfo.GetTypeData(Enum.EnumTypeInfo); end; class function Enum.EnumTypeInfo: PTypeInfo; begin Result := TypeInfo(T); end; class procedure Enum.CheckInRange(const Value: T; const Namespace, MethodName: string); begin if not IntegerInRange(ToInteger(Value)) then Enum.OutOfRange(Value, Namespace, MethodName); end; class function Enum.Count: Integer; begin Result := MaxValue - MinValue + 1; end; class procedure Enum.OutOfRange(const Value: T; const Namespace, MethodName: string); const SEnumOutOfRange = '%s.%s :: %d is out of range for enum %s'; begin raise EEnumOutOfRange.CreateFmt(SEnumOutOfRange, [Namespace, MethodName, ToInteger(Value), TypeName]); end; class function Enum.Parse(const Ordinal: Integer): ContainedEnum; begin Assert(System.SizeOf(Result) <= System.SizeOf(Ordinal)); Move(Ordinal, Result, System.SizeOf(Result)); end; class function Enum.Parse(const EnumValueName: string): ContainedEnum; var Each: ContainedEnum; begin for Each in Enum.AsArray do begin if Enum.ValueName(Each.Value) = EnumValueName then Exit(Each); end; StringParseError(EnumValueName); end; class procedure Enum.NameNotFound(const Identifier: string; const Value: T); const SEnumNameNotFound = 'EnumName not found for %s.%s with identifier %s'; begin raise EEnumNameNotFound.CreateFmt(SEnumNameNotFound, [TypeName, ValueName(Value), Identifier]); end; class procedure Enum.StringParseError(const Value: string); const SCannotParseString = '%s is not defined in enum %s'; begin raise EEnumParseError.CreateFmt(SCannotParseString, [Value, TypeName]); end; class function Enum.AsArray: EnumArray; var I: Integer; begin System.SetLength(Result, Enum.Count); for I := System.Low(Result) to System.High(Result) do Result[I] := Enum.Parse(I); end; {$ENDREGION} constructor ContainedEnum.Create(const Value: T); begin FEnumValue := Value; end; class operator ContainedEnum.Implicit(const Value: T): ContainedEnum; begin Result := ContainedEnum.Create(Value); end; class operator ContainedEnum.Implicit(const Value: Integer): ContainedEnum; begin Result := ContainedEnum.Create(Enum.Parse(Value).Value); end; class operator ContainedEnum.Implicit(const Value: string): ContainedEnum; begin Result := ContainedEnum.Create(Enum.Parse(Value).Value); end; class operator ContainedEnum.Implicit(const Value: ContainedEnum): Integer; begin Result := Enum.ToInteger(Value.FEnumValue); end; class operator ContainedEnum.Implicit(const Value: ContainedEnum): string; begin Result := Enum.ValueName(Value.FEnumValue); end; { class operator ContainedEnum.Implicit(const Value: ContainedEnum): T; begin Result := Value.FEnumValue; end; } function ContainedEnum.ToInteger: Integer; begin Result := Self; end; function ContainedEnum.ToString: string; begin Result := Self; end; end.