I'm trying to create a generic class to which I can use a set of enums to initiate the values inside. For example:
constructor TManager<TEnum>.Create;
var
enum: TEnum;
enumObj: TMyObject;
begin
fMyObjectList:= TObjectDictionary<TEnum,TMyObject>.Create([doOwnsValues],10);
for enum:= Low(TEnum) to High(TEnum) do
begin
enumObj:= TMyObject.Create();
fMyObjectList.Add(enum, enumObj);
end;
end;
Additionally, later methods will fetch objects, via the enum value, for example:
function TManager<TEnum>.Fetch(enum: TEnum): TMyObject;
begin
fMyObjectList.TryGetValue(enum, Result);
end;
However, passing as a generic parameter, delphi doesn't know that TEnum is going to be an enum. Can I enforce that in some way?
As David mentioned the best you can do is at runtime with RTTI.
type
TRttiHelp = record
class procedure EnumIter<TEnum {:enum}>; static;
end;
class procedure TRttiHelp.EnumIter<TEnum {:enum}>;
var
typeInf: PTypeInfo;
typeData: PTypeData;
iterValue: Integer;
begin
typeInf := PTypeInfo(TypeInfo(TEnum));
if typeInf^.Kind <> tkEnumeration then
raise EInvalidCast.CreateRes(@SInvalidCast);
typeData := GetTypeData(typeInf);
for iterValue := typeData.MinValue to typeData.MaxValue do
WhateverYouWish;
end;
Although I don't know how the code behaves when your enum has defined values such as
(a=9, b=19, c=25)
Edit:
If you would like to return iterValue
to the enum, you may use the following function, taken from a enum helper class by Jim Ferguson
class function TRttiHelp.EnumValue<TEnum {:enum}>(const aValue: Integer): TEnum;
var
typeInf: PTypeInfo;
begin
typeInf := PTypeInfo(TypeInfo(TEnum));
if typeInf^.Kind <> tkEnumeration then
raise EInvalidCast.CreateRes(@SInvalidCast);
case GetTypeData(typeInf)^.OrdType of
otUByte, otSByte:
PByte(@Result)^ := aValue;
otUWord, otSWord:
PWord(@Result)^ := aValue;
otULong, otSLong:
PInteger(@Result)^ := aValue;
else
raise EInvalidCast.CreateRes(@SInvalidCast);
end;
end;
You may then use the generically provided as the index to the dictionary in your constructor.
You cannot constrain a generic parameter such that low()
and high()
can be used in the generic class. The only constraints available are class or interface constraints.
To the very best of my knowledge, the language offers no generic way to enumerate over a generic enumerated type. Probably the best you can do is to use RTTI, sacrificing compile time type safety (as illustrated by Tobias).
Having read the comments to Tobias's answer, it seems likely that what you really want here is TObjectDictionary<TEnum,TMyObject>
. That's because you want to be able to find a TMyObject
instance given a TEnum
key. And you want TObjectDictionary
rather than TDictionary
because the former takes over ownership of the TMyObject
instances. You need somebody to free them all when you are done, and you may as well let TObjectDictionary
do it for you.
For an example of the ownership side of this, see @RRUZ's answer here: Example for using Generics.Collections.TObjectDictionary
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With