I have my own class. It has an enumeration property from a base class in Data.DB
:
TProviderFlag = (pfInUpdate, pfInWhere, pfInKey, pfHidden);
TProviderFlags = set of TProviderFlag;
I want to do something like this:
TMyProviderFlag = (pfInUpdate, pfInWhere, pfInKey, pfHidden, pfDummy);
TMyProviderFlags = set of TMyProviderFlag;
So, something like extra types for this enumeration.
But, it should also somehow be possible to get TMyProviderFlag.pfInUpdate
as TProviderFlag.pfInUpdate
, for example (pseudo code):
var my: TMyProviderFlags;
my := [pfHidden, pfDummy];
Table.Field.ProviderFlag := MyInstance.pfHidden;
if pfDummy in MyInstance then ...
Is this possible?
Reason for such a structure - I have code already done (and it passes provider flags), and I have also some properties for fields. It would be a good idea to pass those at the same time.
I have my own class. It has an enumeration property from a base class in Data.DB
:
TProviderFlag = (pfInUpdate, pfInWhere, pfInKey, pfHidden);
TProviderFlags = set of TProviderFlag;
I want to do something like this:
TMyProviderFlag = (pfInUpdate, pfInWhere, pfInKey, pfHidden, pfDummy);
TMyProviderFlags = set of TMyProviderFlag;
So, something like extra types for this enumeration.
But, it should also somehow be possible to get TMyProviderFlag.pfInUpdate
as TProviderFlag.pfInUpdate
, for example (pseudo code):
var my: TMyProviderFlags;
my := [pfHidden, pfDummy];
Table.Field.ProviderFlag := MyInstance.pfHidden;
if pfDummy in MyInstance then ...
Is this possible?
Reason for such a structure - I have code already done (and it passes provider flags), and I have also some properties for fields. It would be a good idea to pass those at the same time.
Share Improve this question edited Mar 11 at 14:42 Remy Lebeau 601k36 gold badges507 silver badges851 bronze badges asked Mar 11 at 11:22 BxOOBxOO 435 bronze badges 02 Answers
Reset to default 3Extending an enumeration is not provided in the standard language. There are workarounds to achieve something similar, but all of them work for specific use cases and fail in others.
One way is to declare the extended enumeration type as sort of an extended subrange:
type
TProviderFlag = (pfInUpdate, pfInWhere, pfInKey, pfHidden);
TProviderFlags = set of TProviderFlag;
const
{$R-} // otherwise compiler will complain
pfDummy = Succ(pfHidden);
type
TMyProviderFlag = pfInUpdate .. pfDummy;
TMyProviderFlags = set of TMyProviderFlag;
Uwe Raabe's answer is correct.
And here's a different solution that works well in some other situations:
type
TBaseEnum = (beOne, beTwo, beThree);
type
TExtEnum = packed record
strict private
Value: Byte;
public
class operator Implicit(const ABaseEnum: TBaseEnum): TExtEnum;
class operator Implicit(const AExtEnum: TExtEnum): TBaseEnum;
end;
{$IF SizeOf(TBaseEnum) <> SizeOf(TExtEnum)}
{$MESSAGE Fatal 'SizeOf(TBaseEnum) <> SizeOf(TExtEnum)'}
{$ENDIF}
const
beFour: TExtEnum = (Value: Succ(Ord(beThree)));
{ TExtEnum }
class operator TExtEnum.Implicit(const AExtEnum: TExtEnum): TBaseEnum;
begin
if InRange(AExtEnum.Value, Ord(Low(TBaseEnum)), Ord(High(TBaseEnum))) then
Result := TBaseEnum(AExtEnum.Value)
else
raise EConvertError.Create('TExtEnum cannot be converted to TBaseEnum.');
end;
class operator TExtEnum.Implicit(const ABaseEnum: TBaseEnum): TExtEnum;
begin
Result.Value := Ord(ABaseEnum);
end;
This leaves TBaseEnum
unchanged, and, under the hood, the new type TExtEnum
is a superset. And this plays along well with Delphi's type system. You can do unsafe casts between the two types if you like (a byte is a byte), but you don't have to: TBaseEnum
will be assignment-compatible with TExtEnum
in both directions:
var
b: TBaseEnum;
x: TExtEnum;
begin
b := beOne;
x := b; // fine
b := x; // fine
x := beOne; // fine
x := beTwo; // fine
x := beThree; // fine
x := beFour; // fine
x := beOne; // fine
b := x; // fine
b := beFour; // run-time error
x := beFour; // fine
b := x; // run-time error
end;