我有这段代码
type
TXSample = (xsType1, xsType2, xsType3, xsType4, xsType5, xsType6, xsType6, xsTyp7, xsType8); // up to FXSample30;
..
private
FXSample = Set of TXSample;
..
published
property Sample: TXSample read FXSample write FXSample;
..
//if Sample has a value of
Sample := [xsType2, xsType4, xsType5, xsType6, xsTyp7];
如何保存/加载Sample的属性?
我想将它保存在数据库中
有可能吗?
答案 0 :(得分:25)
如果您的集合永远不会超过32种可能性(Ord(High(TXSample)) <= 31
),那么将该集合强制转换为Integer
并返回:
type
TXSamples = set of TXSample;
var
XSamples: TXSamples;
begin
ValueToStoreInDB := Integer(XSamples);
Integer(XSamples) := ValueReadFromDB;
end;
更具体地说:SizeOf(TXSamples)
必须精确等于SizeOf(StorageTypeForDB)
。因此,在将Ord(High(TXSample))
强制转换为:
TXSamples
Byte: Ord(High(TXSample)) < 8
Word: 8 <= Ord(High(TXSample)) < 16
Longword: 16 <= Ord(High(TXSample)) < 32
UInt64: 32 <= Ord(High(TXSample)) < 64
答案 1 :(得分:6)
Delphi中无法直接对类变量进行类型转换,但内部Delphi将该集存储为字节值。通过使用无类型移动,可以很容易地将其复制到整数中。请注意,这些函数最多只能达到32(整数的边界)。要增加界限,请改用 Int64 。
function SetToInt(const aSet;const Size:integer):integer;
begin
Result := 0;
Move(aSet, Result, Size);
end;
procedure IntToSet(const Value:integer;var aSet;const Size:integer);
begin
Move(Value, aSet, Size);
end;
演示
type
TMySet = set of (mssOne, mssTwo, mssThree, mssTwelve=12);
var
mSet: TMySet;
aValue:integer;
begin
IntToSet(7,mSet,SizeOf(mSet));
Include(mSet,mssTwelve);
aValue := SetToInt(mSet, SizeOf(mSet));
end;
答案 2 :(得分:4)
Delphi集只是(可能)相关布尔标志的集合。每个布尔标志对应于匹配的序数值是否在集合中。
通过将集合表示为bitset,您当然可以将集合打包为整数值。或者您可以创建该集的文本表示。
但是,这两个选项都使您无法在SQL级别查询数据库。出于这个原因,我建议你将集合中的每个值(即每个布尔标志)表示为数据库表的单独字段(即列)。这为您提供了最强大的数据表示。
答案 3 :(得分:3)
在数据库中存储集合的最简单方法(如注释中提到的@DavidHeffernan)是将您的集转换为位掩码。
在int32(整数)值中,您有32位,可以保存set
最多32个字段;
Delphi在TIntegerSet
中定义了SysUtils
(参见http://docwiki.embarcadero.com/Libraries/en/System.SysUtils.TIntegerSet)类型。它被声明为:
TIntegerSet = set of 0..SizeOf(Integer) * 8 - 1;
所以使用它,将set转换为integer和back(将TIngeterSet转换为整数,反之亦然)很简单;
位掩码也是不错的选择,因为它只是数据库表中的一个INT
字段。
你也可以在你的数据库中创建单独的表来存储集合内容(主表(id,...)和setValuesTable(main_id,setElementValue))(这个选项适合在db查询中使用)
以下是使用TIntegerSet
:
program Project1;
{$APPTYPE CONSOLE}
uses System.SysUtils;
type
TXSample = (xsType1, xsType2, xsType3, xsType4, xsType5, xsType6, xsType7, xsType8);
TSampleSet = set of TXSample;
function SampleSetToInteger(ss : TSampleSet) : integer;
var intset : TIntegerSet;
s : TXSample;
begin
intSet := [];
for s in ss do
include(intSet, ord(s));
result := integer(intSet);
end;
function IntegerToSampleSet(mask : integer) : TSampleSet;
var intSet : TIntegerSet;
b : byte;
begin
intSet := TIntegerSet(mask);
result := [];
for b in intSet do
include(result, TXSample(b));
end;
var xs : TSampleSet;
mask : integer;
begin
xs := [xsType2, xsType6 .. xsType8];
mask := SampleSetToInteger(xs); //integer mask
xs := IntegerToSampleSet(mask);
end.
答案 4 :(得分:3)
就个人而言,我会将该集转换为整数,并将其作为INT
字段存储在数据库中,与其他建议一样。 @teran建议使用TIntegerSet
类型,这是我使用位操作处理本机整数的方法。
请注意,您可以使用SampleInInteger()
来确定枚举中的某个元素是否存在于SampleSetToInteger()
生成的整数掩码中。
以下是代码:
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils;
type
{ .: TXSample :. }
TXSample = (xsType1 = 0, xsType2, xsType3, xsType4, xsType5,
xsType6, xsType7, xsType8); // up to FXSample30;
TXSampleSet = set of TXSample;
// Converts a TXSampleSet to an integer.
function SampleSetToInteger(const S: TXSampleSet): Integer;
var
Sample: TXSample;
begin
Result := 0;
for Sample := Low(TXSample) to High(TXSample) do
if (Sample in S) then
Result := Result or (1 shl Ord(Sample));
end;
// Converts an integer to TXSampleSet.
function IntegerToSampleSet(const Int: Integer): TXSampleSet;
var
I: Integer;
begin
Result := [];
for I := 0 to Ord(High(TXSample)) do
if Int and (1 shl I) <> 0 then
Result := Result + [TXSample(I)];
end;
// Checks if a TXSample is present in the integer.
function SampleInInteger(const S: TXSample; const Int: Integer): Boolean;
begin
Result := Int and (1 shl Ord(S)) <> 0;
end;
var
XSample, XSample1: TXSampleSet;
Tmp: Integer;
begin
XSample := [xsType2, xsType4, xsType5, xsType6, xsType7];
XSample1 := [xsType1];
Tmp := SampleSetToInteger(XSample);
Writeln(Tmp);
XSample1 := IntegerToSampleSet(Tmp);
if (xsType5 in XSample1) then
Writeln('Exists');
if (SampleInInteger(xsType1, Tmp)) then
Writeln('Exists in int');
Readln;
end.
答案 5 :(得分:3)
设置变量可以成功保存到TStream后代。这是一个例子。
只需创建一个新的vcl表单应用程序,向其中添加两个TButton组件,并为每个按钮填写OnClick事件,如下例所示。
这是在XE4中创建的,因此对于其他版本的Delphi,uses子句可能会有所不同,但是通过在uses子句中的每个单元之前删除命名空间指示符来进行更改应该是微不足道的。使用铰接值保存集合类型变量可以使用二进制文件,并且可以使用Delphi轻松保存。换句话说,
还建议看一下TypInfo单元,如果你有源或只是使用提供的功能,这使得解析Set类型到它们的文本表示相当简单,虽然这里没有提供示例。如果您想要保存到配置或ini文件或文本可编辑的持久性格式,建议使用此方法。
下面的一个是我所知道的最简单的一个。查看保存到类似下面的流的集合类型的二进制输出意味着它根据其大小保存在集合的最小可能位图表示中。下面的一个映射到磁盘上的一个字节(值为5),这意味着每个值必须映射到2的幂(seThis = 1,seThat = 2,seTheOther = 4),就像手动创建的常量位掩码值一样。编译器可能会强制遵循强制设置保留其常规的规则。这个例子在Delphi XE4中测试过。
希望有所帮助。
布莱恩约瑟夫约翰斯unit Unit1;
interface
uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
Vcl.StdCtrls;
type
TSomeEnum = (seThis, seThat, seTheOther);
TSomeEnumSet = set of TSomeEnum;
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
SomeSetVar: TSomeEnumSet;
SomeBoolean: Boolean;
SomeInt: Integer;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
SomeSetVar := [seThis, seTheOther];
SomeBoolean := True;
SomeInt := 31415;
with TFileStream.Create('SetSave.bin',fmCreate or fmOpenWrite or fmShareCompat) do
try
Write(SomeSetVar,SizeOf(SomeSetVar));
Write(SomeBoolean,SizeOf(Boolean));
Write(SomeInt,SizeOf(Integer));
finally
Free;
end;
SomeSetVar := [];
SomeInt := 0;
SomeBoolean := False;
end;
procedure TForm1.Button2Click(Sender: TObject);
var
ResponseStr: string;
begin
with TFileStream.Create('SetSave.bin',fmOpenRead or fmShareCompat) do
try
Read(SomeSetVar,SizeOf(SomeSetVar));
Read(SomeBoolean,SizeOf(Boolean));
Read(SomeInt,SizeOf(Integer));
finally
Free;
end;
ResponseStr := 'SomeSetVar = ';
if (seThis in SomeSetVar) then
ResponseStr := ResponseStr + 'seThis ';
if (seThat in SomeSetVar) then
ResponseStr := ResponseStr + 'seThat ';
if (seTheOther in SomeSetVar) then
ResponseStr := ResponseStr + 'seTheOther ';
ResponseStr := ResponseStr + ' SomeBoolean = ' + BoolToStr(SomeBoolean);
ResponseStr := ResponseStr + ' SomeInt = ' + IntToStr(SomeInt);
ShowMessage(ResponseStr);
end;
end.
答案 6 :(得分:1)
或者我们可以让编译器完全忘记类型,然后定义它应该看到的内容(如果我们在编译时知道它会看到什么)。这个解决方案非常糟糕,因为它可以只写一行。
type
// Controls.TCMMouseWheel relies on TShiftState not exceeding 2 bytes in size
TShiftState = set of (ssShift, ssAlt, ssCtrl,
ssLeft, ssRight, ssMiddle,
ssDouble, ssTouch, ssPen,
ssCommand, ssHorizontal);
var
Shifts : TShiftState;
Value : Integer;
begin
Shifts := TShiftState((Pointer(@Value))^):
Value := (PInteger(@Shifts))^;
if ssShift in TShiftState((Pointer(@Value))^) then
Exit;
end;
发生了未使用(顶部)位设置(或不设置)但它对set
操作没有影响(in
,=
,+
,{{ 1}},-
..)。
Delphi中的这一行:
*
在Assembler(Delphi XE6)中是这样的:
Shifts := TShiftState((Pointer(@Value))^);
在Delphi 2007上(lea eax,[ebp-$0c]
mov ax,[eax]
mov [ebp-$06],ax
较小,可以使用TShiftState
)这个汇编程序:
Byte
答案 7 :(得分:0)
您可以使用此单位将set转换为int。如果你需要更多settoint函数,你可以通过查看下面的代码来添加你的。
Set可能只占用1个字节的内存空间。 因此,您可以获得您的Set大小并获得此结果的模块结果。
示例:您的设置大小:您可以获得1个字节的结果 - &gt;
结果:= pINT ^ mod maxVal
您应该通过计算变量类型的maxvalue来获取maxval。
maxVal = Power(2,(8 * sizeof(MySet)-1))
unit u_tool;
interface
uses Graphics;
type
TXSample = (xsType1, xsType2, xsType3, xsType4, xsType5, xsType6, xsType6, xsTyp7, xsType8); // up to FXSample30;
FXSample = Set of TXSample;
function FXSampleToInt(FXSample: FXSample ): Integer;
function IntToFXSample(Value: Integer): FXSample;
function FontStyleToInt(FontStyle: TFontStyles ): Integer;
function IntToFontStyle(Value: Integer): TFontStyles;
implementation
function FXSampleToInt(FXSample: FXSample ): Integer;
var
pInt: PInteger;
begin
pInt := @FXSample;
Result := pInt^;
end;
function IntToFXSample(Value: Integer): FXSample;
var
PFXSample: ^FXSample;
begin
PFXSample := @Value;
Result := PFXSample^;
end;
function FontStyleToInt(FontStyle: TFontStyles ): Integer;
var
pInt: PInteger;
begin
pInt := @FontStyle;
Result := pInt^;
end;
function IntToFontStyle(Value: Integer): TFontStyles;
var
PFontStyles: ^TFontStyles;
begin
PFontStyles := @Value;
Result := PFontStyles^;
end;
end.
答案 8 :(得分:0)
最简单的解决方案 - 直接将集合作为数字变量进行处理。 &#34;绝对&#34;是关键字:
procedure Foo(FXSample: TFXSample);
var
NumericFxSample: Byte absolute FXSample;
begin
WriteLn(YourTextFile, NumericFxSample);//numeric value from a set
end;
如果您的类型超过8位,则需要使用更宽的数字类型,例如单词(一组中最多16项)或双字。