Как сохранить / загрузить набор типов? - PullRequest
17 голосов
/ 04 марта 2012

У меня есть этот код

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?
я хотел бы сохранить его в базе данных.
возможно ли это?

Ответы [ 9 ]

25 голосов
/ 04 марта 2012

Если ваш набор никогда не превысит 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 to:

  • Byte: Ord(High(TXSample)) < 8
  • Word: 8 <= Ord(High(TXSample)) < 16
  • Longword: 16 <= Ord(High(TXSample)) < 32
  • UInt64: 32 <= Ord(High(TXSample)) < 64
6 голосов
/ 16 января 2013

Непосредственное приведение типов к заданной переменной невозможно в 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;
4 голосов
/ 04 марта 2012

Набор Delphi - это просто набор (возможно) связанных логических флагов.Каждый логический флаг соответствует тому, находится ли совпадающее порядковое значение в наборе.

Вы, безусловно, можете упаковать набор в целочисленное значение, представив набор в виде набора битов.Или вы можете создать текстовое представление набора.

Однако оба этих параметра не оставляют вам возможности запрашивать базу данных на уровне SQL.По этой причине я бы посоветовал вам представлять каждое значение в наборе, то есть каждый логический флаг, как отдельное поле (то есть столбец) таблицы базы данных.Это дает вам самое мощное представление данных.

3 голосов
/ 03 января 2014

Заданные переменные могут быть успешно сохранены в потомке TStream.Вот пример.

Просто создайте новое приложение для форм vcl, добавьте в него два компонента TButton и заполните события OnClick для каждой кнопки, как показано в примере ниже.

Это было создано в XE4, поэтому условие использования может отличаться для других версий Delphi, но это должно быть тривиально изменить, удаляя индикаторы пространства имен перед каждым модулем в предложении использования.Сохранение переменной заданного типа с четко сформулированными значениями возможно в двоичном файле и легко с помощью 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.
3 голосов
/ 04 марта 2012

Лично я бы преобразовал набор в целое число и сохранил бы его в базе данных как поле 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.
3 голосов
/ 04 марта 2012

Самый простой способ сохранить набор в базе данных (как упомянуто в комментарии @DavidHeffernan) - это преобразовать ваш набор в битовую маску. в значении int32 (целое) у вас есть 32 бита, и вы можете сохранить set до 32 полей; Delphi имеет TIntegerSet (см. http://docwiki.embarcadero.com/Libraries/en/System.SysUtils.TIntegerSet) тип, определенный в SysUtils. Он объявлен как:

TIntegerSet = set of 0..SizeOf(Integer) * 8 - 1;

поэтому, используя его, просто преобразовать set в целое и обратно (просто приведение TIngeterSet к целому или наоборот);

битовая маска также является хорошим вариантом, потому что это только одно поле INT в таблице базы данных.

также вы можете создать отдельную таблицу в вашей БД для хранения заданного содержимого (основная таблица (id, ...) и setValuesTable (main_id, setElementValue)) (этот параметр подходит для использования в запросах базы данных)

вот пример использования 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.
1 голос
/ 20 апреля 2016

Или мы можем заставить компилятор полностью забыть о типах и затем определить, что он должен видеть (в случае, если мы знаем во время компиляции, что он должен видеть). Это решение настолько ужасно, что может быть записано в одну строку.

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, =, +, -, * ..) .

Эта строка в Delphi:

Shifts := TShiftState((Pointer(@Value))^);

похож на это в Ассемблере (Delphi XE6):

lea eax,[ebp-$0c]
mov ax,[eax]
mov [ebp-$06],ax

В Delphi 2007 (где TShiftState меньше, поэтому можно использовать Byte) этот Ассемблер:

movzx eax,[esi]
mov [ebp-$01],al
0 голосов
/ 20 июня 2016

Самое простое решение - обработка набора непосредственно в виде числовой переменной.«Абсолют» - это ключевое слово:

procedure Foo(FXSample: TFXSample);
var
  NumericFxSample: Byte absolute FXSample;
begin
WriteLn(YourTextFile, NumericFxSample);//numeric value from a set
end;

Если ваш тип длиннее 8 бит, вам нужно использовать более широкий числовой тип, например слово (до 16 элементов в наборе) или слово.

0 голосов
/ 04 декабря 2014

Вы можете использовать это устройство для преобразования набора в int. если вам нужно больше функций settoint, вы можете добавить свои, посмотрев код ниже.

Установка может занимать только 1 байт памяти. Таким образом, вы можете получить размер вашего набора и получить результат как модуль этого результата.

пример: ваш установленный размер: 1 байт, вы можете получить результат ->

Результат: = pINT ^ mod maxVal

Вы должны получить maxval путем вычисления maxvalue типа переменной.

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.
...