Здравствуйте, Mystic, Вы писали:
M>Вместо записи надо создавать класс.
Thanks .
Но, как потом использовать созданный класс? Я хочу сделать как бы хранилище данных, для доступа к которуму достаточно указать название и получить для него все параметры.
Я делал так:
function GetData(title:string):TData;
var i:integer;
begin
i:=Sk.IndexOf(title);
if i<>-1 then
Result:=Data[i];
end;
где
Data: array of TData;
Sk:TStringList,
хранящий названия
Re[3]: Созлание property array, который работает со структур
Здравствуйте, nik-tmn, Вы писали:
NT>Здравствуйте, Mystic, Вы писали:
M>>Вместо записи надо создавать класс. NT>Но, как потом использовать созданный класс? Я хочу сделать как бы хранилище данных, для доступа к которуму достаточно указать название и получить для него все параметры.
Еще можно передавать указатель на структуру.
Почему не работает то, что хотели вы:
Дело в том, что когда запись является возвращаемым значением, в стеке создается результат функции и происходит копирование записи (также возвращаются целочисленные типы, ...). Поэтому изменение в результате функции никак не могут затронуть тот оригинал, что хранится в классе.
Попытайся так:
NT>function GetData(title:string):PData; NT>var i:integer; NT>begin NT> i:=Sk.IndexOf(title); NT> if i<>-1 then NT> Result:=@Data[i]; NT>end;
Сравни
function X1: Integer; // X1 := 5; -- бессмысленно
function X2: PInteger; // X2^ := 5; -- логично
Re[4]: Созлание property array, который работает со структур
В конструкторе создается
New(ZeroData);
и присваиваются значения
ZeroData.SkvName:='';
ZeroData.Bs:=0;
и т.д.
procedure TOriginData.SetOriginData(const skvname:string;value:POriginData_);
var i:integer;
begin
i:=SkvNm.IndexOf(AnsiLowerCase(skvname));
if i=-1 then begin
SkvNm.Add(AnsiLowerCase(skvname));
SetLength(Data,SkvNm.Count);
New(Data[SkvNm.Count-1]);
Data[SkvNm.Count-1]:=@ZeroData;
Data[SkvNm.Count-1]:=@Value;
// Data[SkvNm.Count-1]^.SkvName:='test';
// Data[SkvNm.Count-1].SkvName:=AnsiLowerCase(skvname);
end else begin
Data[i]:=@Value;
end;
end;
Проблема заключается в том, что не обнуляются значения при создании нового параметра, а чаще всего в них мусор, хотя затем при записи -чтении все работает.
По-видимому, что-то не так с присвоением ZeroData.
Кто знает подскажите...
Re[5]: Созлание property array, который работает со структур
Mystic правильно посоветовал "Вместо записи надо создавать класс"
type// вместо record используем класс
// мороки гораздо меньше, а кроме того - поля инициализируются при создании
TOriginData_= class// вместо short string - лучше использовать string
// будет меньше ошибок при случайном переполнении
// и размер будет соответствовать хранимой строке
SkvName,Kust,Plast:string;
sost:boolean;
// лучше вместо single использовать double
// так место не сэкономить, а по быстродействию - лучше
Bs,IsTgo,OssTgo:single;
tgo:TDateTime;
GRPData:GRP__;
ZgrpInd:double;
TP:string;
ro,Ap,mp,Cp,muF,VF,qF:double;
FF:byte;
Mest,PlastGRP:string;
TGrp:TDateTime;
end;
TOriginData=class(TComponent)
protected// здесь будем хранить ВСЁ
SkvNm :TStringList;
procedure SetOriginData(const skvname:string;Value:TOriginData_);
function GetOriginData(const skvname:string):TOriginData_;
public
constructor Create(AOwner:TComponent); override;
destructor Destroy; override;
property ODWell[const skvname:string]:TOriginData_ read GetOriginData write SetOriginData; default;
end;
constructor TOriginData.Create(AOwner:TComponent);
begin
inherited;
SkvNm := TStringList.Create();
SkvNm.Sorted := true;
// будет ругаться на дублировании имен - предупреждение для программиста об ошибках в алгоритмах
SkvNm.Duplicates := dupError;
end;
destructor TOriginData.Destroy;
begin// не забыть почиститьwhile SkvNm.Count > 0 do begin
(SkvNm.Objects[SkvNm.Count - 1]).Free();
end;
inherited;
end;
procedure TOriginData.SetOriginData(const skvname:string; value:TOriginData_);
var i:integer;
begin
i:=SkvNm.IndexOf(skvname); //AnsiLowerCase - не нужноif i = -1 then begin
SkvNm.AddObject(skvname, value);
end else begin// не забыть грохнуть старое значение
(SkvNm.Objects[i]).Free();
SkvNm.Objects[i] := value;
end;
end;
function TOriginData.GetOriginData(const skvname:string):TOriginData_;
var i:integer;
begin// здесь не проверяется на ошибку
Result:=SkvNm.Objects[SkvNm.IndexOf(skvname)];
//----------------------------
// но можно и так
// автосоздание записи при её отсутствии
i:=SkvNm.IndexOf(skvname);
if i = -1 then begin
Result:=TOriginData_.Create();
SkvNm.AddObject(skvname, Result);
end else begin
Result:=SkvNm.Objects[i];
end;
end;
Евгений
Re[6]: Созлание property array, который работает со структур
Спасибо, почти все работает, (с указателями не получалось хранить разные значения — все записывалось по одному адресу ) но есть еще вопросы.
M> // вместо short string - лучше использовать string
M> // будет меньше ошибок при случайном переполнении
M> // и размер будет соответствовать хранимой строке
Я использовал такой тип, чтобы в дальнейшем сохранять/загружать данные через TMemoryStream
M>destructor TOriginData.Destroy;
M>begin
M> // не забыть почистить
M> while SkvNm.Count > 0 do begin
M> (SkvNm.Objects[SkvNm.Count - 1]).Free();
M> end;
В этом месте происходит AV Error, но не понятно почему...
А нельзя просто: SkvNm.Free; ?
Best regards,
Nik
Re[7]: Созлание property array, который работает со структур
Здравствуйте, nik-tmn, Вы писали:
NT>Да, как нибудь можно, чтобы при попытке чтения несуществующего параметра, выдавался нулевой результат, а не создавался параметр?
А что значит "нулевой" — nil?
function TOriginData.GetOriginData(const skvname:string):TOriginData_;
var i:integer;
begin
i:=SkvNm.IndexOf(skvname);
if i = -1 then begin
Result := nil;
end else begin
Result := SkvNm.Objects[i];
end;
end;
Надо добавить к TOriginData_ методы SaveToStream(Str: TStream) и LoadFromStream(Str: TStream), в которых
по очереди сохранять и восстанавливать поля.
PS. Я думаю, может возникнуть вопрос — а зачем всё это надо, если с record было всё легко и просто?
Если да, то ответ, наверное, — а сейчас всё прозрачно и красиво. Нет?
Евгений
Re[10]: Созлание property array, который работает со структу
M>А что значит "нулевой" — nil?
Нет не nil, а чтобы при к любым переменным выдавалось 0
(ODWell['1].Bs выдавало бы 0 и т.д.)
Написал вот такие обработчики:
{TOriginData_}function TOriginData_.SaveToStream(var Str:TMemoryStream):boolean;
var Title:TTitle;
tmp:TOriginDataRecord;
begin
try
tmp.SkvName:=SkvName; tmp.Kust:=Kust; tmp.Plast:=Plast;
tmp.sost:=sost;
tmp.Bs:=Bs; tmp.IsTgo:=IsTgo; tmp.OssTgo:=OssTgo; tmp.tgo:=tgo;
tmp.GRPData:=GRPData;
tmp.ZgrpInd:=ZgrpInd;
tmp.TP:=TP; tmp.ro:=ro; tmp.Ap:=Ap; tmp.mp:=mp;
tmp.Cp:=Cp; tmp.muF:=muF; tmp.VF:=VF; tmp.qF:=qF;
tmp.FF:=FF;
tmp.Mest:=Mest; tmp.PlastGRP:=PlastGRP;
tmp.TGrp:=TGrp;
Title.ID:=0; Title.CountByte:=sizeof(integer);
str.Write(Title,sizeof(Title));
Title.ID:=1; Title.CountByte:=sizeof(tmp);
str.Write(Title,sizeof(Title));
str.Write(tmp,Title.CountByte);
Title.ID:=-1; Title.CountByte:=sizeof(integer);
str.Write(Title,sizeof(Title));
Result:=true;
except
Result:=false;
end;
end;
function TOriginData_.LoadFromStream(var Str:TMemoryStream):boolean;
var Title:TTitle;
tmp:TOriginDataRecord;
begin
try
str.Read(Title,sizeof(Title));
if Title.ID<>0 then Raise Exception.Create('Error loading data');
str.Read(Title,sizeof(Title));
if Title.ID<>1 then Raise Exception.Create('Error loading data');
str.Read(tmp,Title.CountByte);
str.read(Title,sizeof(Title));
if Title.ID<>-1 then Raise Exception.Create('Error loading data');
SkvName:=tmp.SkvName; Kust:=tmp.Kust; Plast:=tmp.Plast;
sost:=tmp.sost;
Bs:=tmp.Bs; IsTgo:=tmp.IsTgo; OssTgo:=tmp.OssTgo; tgo:=tmp.tgo;
GRPData:=tmp.GRPData;
ZgrpInd:=tmp.ZgrpInd;
TP:=tmp.TP; ro:=tmp.ro; Ap:=tmp.Ap; mp:=tmp.mp;
Cp:=tmp.Cp; muF:=tmp.muF; VF:=tmp.VF; qF:=tmp.qF;
FF:=tmp.FF;
Mest:=tmp.Mest; PlastGRP:=tmp.PlastGRP;
TGrp:=tmp.TGrp;
Result:=true;
except
Result:=false;
end;
end;
А в классе TOriginData:
function TOriginData.SaveToStream(var Str:TMemoryStream):boolean;
var i,count:integer;
Buffer:TOriginData_;
begin
Buffer:=TOriginData_.Create;
try
if Str=nil then Raise Exception.Create('No stream');
Str.Clear;
count:=SkvNm.Count;
//writing header
Str.Write(Count,SizeOf(Integer));
//writing datafor i:=0 to Count-1 do
(SkvNm.Objects[i] as TOriginData_).SaveToStream(str);
//writing closing data
Count:=-1;
Str.Write(Count,SizeOf(Integer));
Str.Position:=0;
Result:=true;
Buffer.Free;
except
Result:=false;
end;
end;
function TOriginData.LoadFromStream(Str:TMemoryStream):boolean;
var i,count:integer;
Buffer:TOriginData_;
begin
Buffer:=TOriginData_.Create;
try
if Str=nil then Raise Exception.Create('No stream');
Str.Position:=0;
//getting records count
Str.Read(Count,SizeOf(Integer));
if count<=0 then Raise Exception.Create('No Data');
//clearing previous
SkvNm.Clear;
//reading recordsfor i:=0 to Count-1 do begin
Buffer.LoadFromStream(str);
ODWell[Buffer.SkvName]:=Buffer;
end;
//checking closing data
Str.Read(Count,SizeOf(Integer));
if Count<>-1 then Raise Exception.Create('OriginData CRC Error');
Str.Position:=0;
Result:=true;
except
Result:=false;
end;
end;
Сохраняет отлично, а при загрузке возникает ошибка AV(даже не при загрузке, а при выходе из программы. Ошибка возникает в процедуре LoadFromStream в месте:
for i:=0 to Count-1 do begin
Buffer.LoadFromStream(str);
ODWell[Buffer.SkvName]:=Buffer;
end;
Как я думаю, происходит не копирование, а просто присвоение адреса? Как это можно пофиксить?
M>Если да, то ответ, наверное, — а сейчас всё прозрачно и красиво. Нет?
С этим полностью согласен
Best regards,
Nik
Re[11]: Созлание property array, который работает со структу
...
NT>Сохраняет отлично, а при загрузке возникает ошибка AV(даже не при загрузке, а при выходе из программы. Ошибка возникает в процедуре LoadFromStream в месте: NT>
NT> for i:=0 to Count-1 do begin
NT> Buffer.LoadFromStream(str);
NT> ODWell[Buffer.SkvName]:=Buffer;
NT> end;
NT>
NT>Как я думаю, происходит не копирование, а просто присвоение адреса? Как это можно пофиксить?
Да, присвоение ссылки на один и тот же экземпляр Buffer. Возможно два выхода:
1) Создавать каждый раз новый Buffer:
for i:=0 to Count-1 do begin// вот здесь
Buffer := TOriginData_.Create();
Buffer.LoadFromStream(str);
ODWell[Buffer.SkvName]:=Buffer;
end;
но, с учетом "красоты" и избавления от возможных ошибок (можно забыть создавать новый экземпляр), имхо, лучше
2) В методе SetOriginData, производить копирование полей входного value в поля, внутри создаваемого, объекта:
procedure TOriginData.SetOriginData(const skvname:string; value:TOriginData_);
var temp : TOriginData_;
begin// получаем/генерируем экземпляр TOriginData_
temp := GetOriginData(skvname);
// и просто копируем поля из value в temp
// по заведенной традиции, прячем этот процесс
// в метод TOriginData_.Assign
temp.Assign(value);
// или так
//GetOriginData(skvname).Assign(value); end;