delphi - ¿Cómo guardar/cargar Conjunto de Tipos?
types typecast-operator (9)
En Delphi, no es posible realizar una conversión de tipo de letra directamente, pero Delphi almacena el conjunto como un valor de byte. Al usar un movimiento sin tipo, es fácil copiarlo en un entero. Tenga en cuenta que estas funciones solo alcanzan un tamaño de 32 (límites de un entero). Para aumentar los límites, utilice Int64 en su lugar.
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;
Manifestación
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;
Tengo este codigo
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];
¿Cómo puedo guardar / cargar la propiedad de Muestra?
Me gustaría guardarlo en la base de datos.
¿Es posible?
La forma más fácil de almacenar el conjunto en la base de datos (como se menciona en @DavidHeffernan en el comentario) es convertir su conjunto en máscara de bits. en el valor int32 (entero), tiene 32 bits y puede guardar hasta 32 campos; Delphi tiene el tipo TIntegerSet
(consulte http://docwiki.embarcadero.com/Libraries/en/System.SysUtils.TIntegerSet ) definido en SysUtils
. se declara como:
TIntegerSet = set of 0..SizeOf(Integer) * 8 - 1;
así que, al usarlo, es fácil convertir el conjunto en entero y viceversa (solo se convierte TIngeterSet en entero o viceversa);
bit-mask también es una buena opción porque es solo un campo INT
en su tabla de base de datos.
también puede crear una tabla separada en su base de datos para almacenar el contenido del conjunto (tabla principal (id, ...) y setValuesTable (main_id, setElementValue)) (esta opción es buena para usar en consultas de base de datos)
Aquí hay un ejemplo del uso de 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.
La solución más simple: proceder con el conjunto directamente como variable numérica. El "absoluto" es una palabra clave:
procedure Foo(FXSample: TFXSample);
var
NumericFxSample: Byte absolute FXSample;
begin
WriteLn(YourTextFile, NumericFxSample);//numeric value from a set
end;
Si su tipo es más ancho que 8 bits, necesita usar un tipo numérico más ancho como palabra (hasta 16 elementos en un conjunto) o dword.
Las variables establecidas se pueden guardar con éxito en un descendiente de TStream. Aquí hay un ejemplo.
Simplemente cree una nueva aplicación de formularios vcl, agregue dos componentes TButton y complete los eventos OnClick para cada botón como se ilustra en el siguiente ejemplo.
Esto se creó en XE4, por lo que la cláusula de usos puede ser diferente para otras versiones de Delphi, pero eso debería ser trivial de cambiar al eliminar los indicadores de espacio de nombres antes de cada unidad en la cláusula de usos. Guardar una variable de tipo conjunto con valores articulados es posible en un archivo binario y fácilmente con Delphi. En otras palabras,
También se sugiere echar un vistazo a la unidad TypInfo si tiene la fuente o simplemente usar las funciones proporcionadas que hacen que la disección de los tipos de Set a su representación de texto sea bastante simple, aunque aquí no se proporciona ningún ejemplo. Se sugiere si desea incluir guardar en un archivo de configuración o ini o en un formato de persistencia que se pueda editar con texto.
El de abajo es el más simple que conozco. Mirar la salida binaria de un tipo de conjunto guardado en una secuencia como la que se muestra a continuación implica que se guarda en la representación de mapa de bits más pequeña posible para el conjunto en función de su tamaño. El que se muestra a continuación se asigna a un byte en el disco (el valor es 5), lo que significa que cada valor debe asignarse a una potencia de 2 (seThis = 1, seThat = 2, seTheOther = 4) al igual que los valores enmascarados constantes creados manualmente. El compilador probablemente hace cumplir que sigue reglas que obligan a mantener su ordinalidad. Este ejemplo fue probado y funciona en Delphi XE4.
Espero que ayude.
Brian Joseph Johns
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.
O podemos hacer que el compilador se olvide completamente de los tipos y luego definir lo que debería ver (en caso de que sepamos en tiempo de compilación lo que debería ver). Esta solución es tan horrible como se puede escribir en una sola línea.
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;
Sucede que los bits no utilizados (arriba) se establecen (o no) pero no tiene influencia en las operaciones de set
( in
, =
, +
, -
, *
..).
Esta línea en Delfos:
Shifts := TShiftState((Pointer(@Value))^);
Es así en Assembler (Delphi XE6):
lea eax,[ebp-$0c]
mov ax,[eax]
mov [ebp-$06],ax
En Delphi 2007 (donde TShiftState
es más pequeño para que se pueda usar Byte
), este ensamblador:
movzx eax,[esi]
mov [ebp-$01],al
Personalmente, convertiría el conjunto en un entero y lo almacenaría en la base de datos como un campo INT
, como otros sugirieron. @teran sugirió usar el tipo TIntegerSet
, y aquí está mi enfoque trabajando con enteros nativos usando operaciones de bit.
Tenga en cuenta que puede usar SampleInInteger()
para determinar si un elemento determinado de la enumeración está presente en la máscara de enteros generada por SampleSetToInteger()
.
Aquí está el código:
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.
Puede utilizar esta unidad para convertir set a int. Si necesita más funciones settoint, puede agregar la suya buscando el código a continuación.
El conjunto puede tomar solo 1 byte de espacio de memoria. Para que pueda obtener su tamaño de Set y obtener el resultado como modula de este resultado.
Ejemplo: su tamaño de conjunto: 1 byte puede obtener el resultado ->
Resultado: = pINT ^ mod maxVal
Debe obtener maxval calculando maxvalue de tipo variable.
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.
Siempre que su conjunto nunca exceda las 32 posibilidades ( Ord(High(TXSample)) <= 31
), entonces es perfectamente Ord(High(TXSample)) <= 31
encasillar el conjunto en un Integer
y viceversa:
type
TXSamples = set of TXSample;
var
XSamples: TXSamples;
begin
ValueToStoreInDB := Integer(XSamples);
Integer(XSamples) := ValueReadFromDB;
end;
Para ser más específicos: SizeOf(TXSamples)
tiene que ser exactamente igual a SizeOf(StorageTypeForDB)
. Por lo tanto, los siguientes rangos se aplican a Ord(High(TXSample))
al realizar la conversión de TXSamples
de TXSamples
a:
-
Byte: Ord(High(TXSample)) < 8
-
Word: 8 <= Ord(High(TXSample)) < 16
-
Longword: 16 <= Ord(High(TXSample)) < 32
-
UInt64: 32 <= Ord(High(TXSample)) < 64
Un conjunto de Delphi es simplemente una colección de (posiblemente) banderas booleanas relacionadas. Cada indicador booleano corresponde a si el valor ordinal coincidente está o no en el conjunto.
Ciertamente, podría empaquetar un conjunto en un valor entero representando el conjunto como un conjunto de bits. O puedes crear una representación textual del conjunto.
Sin embargo, estas dos opciones no le permiten tener una capacidad manejable para consultar la base de datos en el nivel SQL. Por este motivo, le aconsejo que represente cada valor en el conjunto, es decir, cada indicador booleano, como un campo separado (es decir, columna) de la tabla de la base de datos. Esto le da la representación más poderosa de los datos.