【问题标题】:How can I avoid EInvalidPointer error when using TObjectDictionary in Delphi?在 Delphi 中使用 TObjectDictionary 时如何避免 EInvalidPointer 错误?
【发布时间】:2015-10-14 08:48:35
【问题描述】:

程序通过窗口消息接收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;

更新的代码在FProductDictionary.Remove(StringKey); 上出现EInvalidPointer 错误我做错了什么?

【问题讨论】:

    标签: delphi tdictionary


    【解决方案1】:

    您提供的代码不完整。您没有显示 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&lt;TProductItem&gt; 使代码对读者更清晰。读者可以查看TObjectList&lt;TProductItem&gt; 并立即知道它是什么,因为TObjectList&lt;T&gt; 是一种无处不在的类型。

    【讨论】:

    • 谢谢。我试图将 TProductItemList 的新实例添加到 FProductDictionary。但是 FProductList 是作为全局变量用于堆叠产品信息数据。因为产品数据每 1 秒传入一次。如何堆叠数据以将数据列表添加到 FProductDIctionary?
    • 在某处堆积数据。当您准备好添加它时,创建一个新的产品列表,将堆积的数据转移到那里,添加到字典,然后清除堆积的数据以备更多到达。
    • @DavidHeffernan 谢谢。我会尝试。然后我再在这里回复。
    • @MartynA 我认为Dictionary 更适合我的情况。
    • @MartynA 表示使用队列来堆叠传入的数据。我认为我不能真正为您编写其余的代码。我已经向您展示了您的运行时错误来自哪里,这就是您所要求的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-02
    • 1970-01-01
    • 2013-12-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多