delphi - update - ¿Hay alguna manera de actualizar un campo en un registro sabiendo el nombre y el valor del campo?
modificar registro sql server (2)
Lo que sabe Delphi 7 RTTI, y se puede recuperar de TypeInfo(aRecordType)
, es:
- El nombre del tipo de registro;
- El tamaño global récord;
- El desplazamiento y el tipo de cada variable contada de referencia dentro del registro (cadena / variante / cadena más amplia / matriz dinámica / otro registro anidado que contiene variables contadas de referencia).
La información más reciente es necesaria para liberar la memoria utilizada por cada variable de referencia dentro del registro, o copiar el contenido del registro, en tiempo de ejecución. La inicialización del registro también se realiza en el código generado por el compilador (si el registro se crea en la pila), ya sea a través del método _InitializeRecord()
, con un relleno global a 0 cuando se instancia una clase o una matriz dinámica.
Es igual para los tipos de record
y de object
, en todas las versiones de Delphi.
Puede observar que hay un error en la versión moderna de Delphi (incluyendo Delphi 2009 y 2010 al menos), que a veces no crea el código para inicializar objetos en la pila. Tendrá que usar el registro en su lugar, pero romperá la compatibilidad con la versión anterior de Delphi. :(
Aquí está la estructura utilizada para almacenar estos datos RTTI:
type
TFieldInfo = packed record
TypeInfo: ^PDynArrayTypeInfo; // information of the reference-counted type
Offset: Cardinal; // offset of the reference-counted type in the record
end;
TFieldTable = packed record
Kind: byte;
Name: string[0]; // you should use Name[0] to retrieve offset of Size field
Size: cardinal; // global size of the record = sizeof(aRecord)
Count: integer; // number of reference-counted field info
Fields: array[0..0] of TFieldInfo; // array of reference-counted field info
end;
PFieldTable = ^TFieldTable;
Usando estos datos, aquí está, por ejemplo, lo que puede hacer:
- Algunas versiones de ASM de bajo nivel optimizadas de Delphi 7 System.pas , correspondientes a los registros (incluida una función más rápida de CopyRecord );
- Una forma de acceder, guardar y cargar el contenido de los registros (también dentro de arreglos dinámicos, proporcionando métodos de tipo TList, y más, para un conjunto dinámico de registros).
Por ejemplo, aquí es cómo se pueden comparar dos registros del mismo tipo, utilizando este RTTI:
/// check equality of two records by content
// - will handle packed records, with binaries (byte, word, integer...) and
// string types properties
// - will use binary-level comparison: it could fail to match two floating-point
// values because of rounding issues (Currency won''t have this problem)
function RecordEquals(const RecA, RecB; TypeInfo: pointer): boolean;
var FieldTable: PFieldTable absolute TypeInfo;
F: integer;
Field: ^TFieldInfo;
Diff: cardinal;
A, B: PAnsiChar;
begin
A := @RecA;
B := @RecB;
if A=B then begin // both nil or same pointer
result := true;
exit;
end;
result := false;
if FieldTable^.Kind<>tkRecord then
exit; // raise Exception.CreateFmt(''%s is not a record'',[Typ^.Name]);
inc(PtrUInt(FieldTable),ord(FieldTable^.Name[0]));
Field := @FieldTable^.Fields[0];
Diff := 0;
for F := 1 to FieldTable^.Count do begin
Diff := Field^.Offset-Diff;
if Diff<>0 then begin
if not CompareMem(A,B,Diff) then
exit; // binary block not equal
inc(A,Diff);
inc(B,Diff);
end;
case Field^.TypeInfo^^.Kind of
tkLString:
if PAnsiString(A)^<>PAnsiString(B)^ then
exit;
tkWString:
if PWideString(A)^<>PWideString(B)^ then
exit;
{$ifdef UNICODE}
tkUString:
if PUnicodeString(A)^<>PUnicodeString(B)^ then
exit;
{$endif}
else exit; // kind of field not handled
end;
Diff := sizeof(PtrUInt); // size of tkLString+tkWString+tkUString in record
inc(A,Diff);
inc(B,Diff);
inc(Diff,Field^.Offset);
inc(Field);
end;
if CompareMem(A,B,FieldTable.Size-Diff) then
result := true;
end;
Por lo tanto, para su propósito, lo que Delphi 7 RTTI podría hacerle saber en tiempo de ejecución, es la posición de cada cadena dentro de un registro. Usando el código anterior, puede crear fácilmente una función usando el índice de campo:
procedure UpdateStringField(StringFieldIndex: integer; const FieldValue: string);
Pero simplemente no tiene la información necesaria para implementar su solicitud:
- Los nombres de los campos no se almacenan dentro del RTTI (solo el nombre del tipo de registro global, e incluso no siempre AFAIK);
- Solo los campos contados de referencia tienen un desplazamiento, no otros campos de tipo simple (como entero / doble ...).
Si realmente necesita esta función, la única solución bajo Delphi 7 es usar no registros, sino clases.
En Delphi 7, si creas una clase con campos publicados, tendrás toda la información necesaria para todos los campos publicados. A continuación, puede actualizar dicho contenido de campo publicado. Esto es lo que hace el tiempo de ejecución de VCL al deserializar el contenido .dfm en instancias de clase o con un enfoque de ORM .
Dado un registro:
MyRecord = record
Company: string;
Address: string;
NumberOfEmplyees: integer;
puedes escribir una llamada de función como
function UpdateField(var FieldName: string; FieldValue: variant): bool;
así que eso:
UpdateField(''Company'', ''ABC Co'');
actualizaría MyRecord.Company a ''ABC Co''?
Busqué un ejemplo pero todo lo que encontré es para una base de datos. Cualquier ayuda que me señale en la dirección correcta es apreciada.
Gracias carlos
Necesita las versiones modernas de Delphi para hacer lo que pide sin tener que recurrir a la codificación manual de las búsquedas, por ejemplo, a través de una tabla.
El RTTI actualizado introducido en Delphi 2010 puede admitir lo que está buscando, pero no hay nada en Delphi 7 que haga esto por los registros.