How to store a set in a TStringList Object?
I'm trying to store a set inside the object property (and read it) of a TStringList (I will also use it to store text associated to the set) but I get a invalid typecast for the set.
What's the best way to store a set inside a StringList object? Also, will this object need to be freed when destroying the StringList?
Here's some example code:
type
TDummy = (dOne, dTwo, dThree);
TDummySet = set of TDummy;
var
DummySet: TDummySet;
SL: TStringList;
begin
开发者_StackOverflow社区SL := TStringList.Create;
Try
DummySet := [dOne, dThree];
SL.AddObject('some string', TObject(DummySet)); // Doesn't work. Invalid typecast
Finally
SL.Free;
End;
end;
First read the other answers - probably you'll find a less hacky solution.
But FTR: You can write
SL.AddObject('some string', TObject(Byte(DummySet)));
and
DummySet := TDummySet(Byte(SL.Objects[0]));
if you really want.
Note: You'll have to change the keyword Byte
if you add enough elements to the TDummySet type. For example, if you add six more elements (so that there is a total of nine) you need to cast to Word
.
I can't add non objects on that case.
What you can do, is create an object that have TDummySet as Field. Something like
TExemple = class
DummySet = TDummySet;
end;
Or you can use a different approach:
Declarations:
TDummy = (dOne, dTwo, dThree);
TDummySet = set of TDummy;
PDummySet = ^TDummySet;
How to use:
var
DummySet: PDummySet;
begin
New(DummySet);
DummySet^ := [dOne, dThree];
You should not store a set via TStringList.Objects because what Objects use (TObject) is a 32 bit value type and sets can be represented up to 256 bits depending on the size of the set. That's probably why the compiler doesn't even allow casting.
A better way to serialize sets is using RTTI. I am not sure where VCL exposes its builtin set serialization mechanism but JCL has a JclRTTI unit with JclSetToStr and JclStrToSet functions.
var
fs: TFontStyles;
begin
JclStrToSet(TypeInfo(TFontStyles), fs, 'fsBold, fsItalic'); // from string
Showessage(JclSetToStr(TypeInfo(TFontStyles), fs)); // to string
end;
I don't think a stringlist is the way to go. Why not an array of TDummySet? And no, there is no need to free it because the set is not an object.
var
Test: Array of TDummySet;
SetLength(Test, 2);
Test[0] := [dOne, dThree];
Test[1] := [dTwo];
When you're done:
SetLength(Test, 0);
You cannot make a typecast from your set to a TObject, because your variable is not a pointer.
You have to store a pointer to your variable in the TStringList. In that case, you'll have to allocate and deallocate it manually too.
Try something like this:
type
TEnum = (one, two, three);
TSet = set of TEnum;
PSet = ^TSet;
var s: TStringList;
p: PSet;
begin
s := TStringList.Create;
p := AllocMem(SizeOf(TSet));
p^ := [two, three];
S.AddObject('a', TObject(p));
// bla bla bla
// Here you read the set in the string list
if (two in PSet(S.Objects[0])^)) then begin
// your checks here
end
...
精彩评论