-1

程序product information datas通过窗口接收消息。在过程中处理的传入数据TProductInstance.PutProductData

产品信息包含日期、名称、价格。我想将数据存储为TObjectDictionary. 键是与产品相同的日期,值是与TObjectList 一样的产品信息数据列表。此外,我只想在最近 7 天内维护数据。顺便说一句,当我从 TObjectDictionary 中删除项目进行维护时,会出现如下错误。

第一次机会例外,价格为 75214598 美元。
带有消息“无效指针操作”的异常类 EInvalidPointer。处理 product.exe (3848)。

这是由FProductDictionary.Remove(StringKey);.

如何EInvalidPointer在维护最新的 7 天数据时避免错误?

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);我做错了什么?

4

1 回答 1

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>一种无处不在的类型。

于 2015-10-14T08:55:44.667 回答