在Delphi中使用TObjectDictionary时,如何避免EInvalidPointer错误?

时间:2015-10-14 08:48:35

标签: delphi tdictionary

程序通过窗口消息接收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);我错了什么?

1 个答案:

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