程序通过窗口消息接收product information datas
。
在TProductInstance.PutProductData
程序中处理的传入数据。
产品信息包含日期,名称,价格。
我想将数据存储为TObjectDictionary
。键是产品的相同日期,值是产品信息数据列表为TObjectList。
另外,我想在最近7天内维护数据。
顺便说一句,当我从TObjectDictionary中删除项目以进行维护时,会发生如下错误。
$ 75214598的第一次机会异常。
例外类EInvalidPointer,带有消息'无效指针操作'。处理product.exe(3848)。
这是由FProductDictionary.Remove(StringKey);
引起的。
如何维护最新7天数据时出现EInvalidPointer
错误?
type
TProductItem = class(TObject)
private
FDate: String;
FName: String;
FPrice: Integer;
procedure SetDate(const value: String);
procedure SetName(const value: String);
procedure SetPrice(const value: Integer);
public
property Date: String read FDate write SetDate;
property Name: String read FName write SetName;
property Price: Integer read FPrice write SetPrice;
constructor Create(const date, name: String; const price: Integer);
end;
TProductItemList = class(TObjectList<TProductItem>);
type
TProductInstance = class(TObject)
private
public
FLatestDate: String;
FProductList: TProductItemList;
FProductDictionary: TObjectDictionary<String, TProductItemList>;
constructor Create;
destructor Destroy; override;
procedure PutProductData(var Data: LP_Data);
end;
implementation
constructor TProductInstance.Create;
begin
FLatestDate := '';
FProductList := TProductItemList.Create;
FProductDictionary := TObjectDictionary<String, TProductItemList>.Create([doOwnsValues]);
end;
procedure TProductInstance.PutProductData(var Data: LP_Data);
var
StringKey: String;
begin
if (Trim(LP_Data^.date) <> FLatestDate) then
begin
FProductDictionary.AddOrSetValue(Trim(LP_Data^.date), FProductList);
for StringKey in FProductDictionary.Keys do
begin
if (GetDateToInt(Trim(LP_Data^.date)) - GetDateToInt(FLatestDate) > 7) then
FProductDictionary.Remove(StringKey);
end;
FProductList.Free;
end;
FProductList.Add(TProductItem.Create(Trim(LP_Data^.date), Trim(LP_Data^.name), Trim(LP_Data^.price)));
FLatestDate := Trim(LP_Data^.date);
end;
已更新
type
TProductItem = class(TObject)
private
FDate: String;
FName: String;
FPrice: Integer;
procedure SetDate(const value: String);
procedure SetName(const value: String);
procedure SetPrice(const value: Integer);
public
property Date: String read FDate write SetDate;
property Name: String read FName write SetName;
property Price: Integer read FPrice write SetPrice;
constructor Create(const date, name: String; const price: Integer);
end;
type
TProductInstance = class(TObject)
private
public
FLatestDate: String;
FProductList: TObjectList<TProductItem>;
FProductDictionary: TObjectDictionary<String, TObjectList<TProductItem>>;
constructor Create;
destructor Destroy; override;
procedure PutProductData(var Data: LP_Data);
end;
implementation
constructor TProductInstance.Create;
var
LProductItem: TProductItem;
LProductItemList: TObjectList<TProductItem>;
LStringList: TStringList;
begin
FLatestDate := '';
FProductList := TObjectList<TProductItem>.Create;
FProductDictionary := TObjectDictionary<String, TObjectList<TProductItem>>.Create([doOwnsValues]);
end;
procedure TProductInstance.PutProductData(var Data: LP_Data);
var
StringKey: String;
begin
FProductList.Add(TProductItem.Create(Trim(LP_Data^.date), Trim(LP_Data^.name), Trim(LP_Data^.price)));
if (Trim(LP_Data^.date) <> FLatestDate) then
begin
LProductItemList := TObjectList<ProductItem>.Create;
for LProductItem in FProductList do
begin
LProductItemList.Add(LProductItem);
end;
FProductDictionary.AddOrSetValue(Trim(LP_Data^.date), LProductItemList);
FProductList.Clear;
LStringList := TStringList.Create;
for StringKey in FProductDictionary.Keys do
begin
if (GetDateToInt(Trim(LP_Data^.date)) - GetDateToInt(FLatestDate) > 7) then
begin
LStringList.Add(StringKey);
end;
end;
for StringKey in LStringList do
begin
FProductDictionary.Remove(StringKey);
end;
FreeAndNil(LStringList);
end;
end;
更新后的代码EInvalidPointer
错误FProductDictionary.Remove(StringKey);
我错了什么?
答案 0 :(得分:3)
您提供的代码不完整。您没有显示TProductInstance
的析构函数。对于这样的问题,你应该总是提供一个简单的MCVE。这在单个控制台.dpr文件中很容易实现。
看看我们可以看到的内容,很明显代码中的生命周期管理被破坏了。让我们批评这种方法。
procedure TProductInstance.PutProductData(var Data: LP_Data);
var
StringKey: String;
begin
if (Trim(LP_Data^.date) <> FLatestDate) then
begin
FProductDictionary.AddOrSetValue(Trim(LP_Data^.date), FProductList);
for StringKey in FProductDictionary.Keys do
begin
if (GetDateToInt(Trim(LP_Data^.date)) - GetDateToInt(FLatestDate) > 7) then
FProductDictionary.Remove(StringKey);
end;
FProductList.Free;
end;
FProductList.Add(TProductItem.Create(Trim(LP_Data^.date), Trim(LP_Data^.name),
Trim(LP_Data^.price)));
FLatestDate := Trim(LP_Data^.date);
end;
因为FProductDictionary
拥有其值,所以
FProductDictionary.AddOrSetValue(Trim(LP_Data^.date), FProductList);
然后FProductDictionary
成为FProductList
的所有者。这意味着你不应该永远消灭FProductList
。但是,你做到了这一点:
FProductList.Free;
所以你要多次摧毁FProductList
这是一个明显的错误。
下一步该怎么做?你需要处理终身问题。我无法从这里提供的代码中了解到您要实现的目标,以及如何管理生命周期。您需要确定谁负责拥有什么,并确保您遵守明确的终身管理政策。
从表面上看,我最好的猜测是你需要删除FProductList
字段。当您需要向FProductDictionary
添加新项目时,实例化TProductItemList
的新实例,填充它并将其添加到字典中。此时,字典控制TProductItemList
的生命周期。
作为最后一条评论,我建议类型TProductItemList
毫无意义。我会删除它。使用TObjectList<TProductItem>
使代码更清晰。读者可以查看TObjectList<TProductItem>
并立即知道它是什么,因为TObjectList<T>
是一种无处不在的类型。