5
votes

I have this spike to test TPair. You can copy+paste on a new Delphi XE Console-app. I have marked the line with the exception:

Project Project1.exe raised exception class EAccessViolation with message 'Access violation at address 0045042D in module 'Project1.exe'. Read of address A9032D0C.

Any Idea ?

Thanks.

program Project1;

{$APPTYPE CONSOLE}

uses
  SysUtils,
  Generics.Defaults,
  Generics.Collections;

type
  TProduct = class
  private
    FName: string;
    procedure SetName(const Value: string);
  published
  public
    property Name: string read FName write SetName;
  end;

type
  TListOfProducts = TObjectDictionary<TProduct, Integer>;

{ TProduct }

procedure TProduct.SetName(const Value: string);
begin
  FName := Value;
end;


var
  MyDict: TListOfProducts;
  MyProduct1: TProduct;
  MyProduct2: TProduct;
  MyProduct3: TProduct;
  APair: TPair<TProduct, Integer>;
  aKey: string;

begin
  try
    MyDict := TListOfProducts.Create([doOwnsKeys]);
    MyProduct1 := TProduct.Create;
    MyProduct1.Name := 'P1';
    MyProduct2 := TProduct.Create;
    MyProduct2.Name := 'P2';
    MyProduct3 := TProduct.Create;
    MyProduct3.Name := 'P3';

    MyDict.Add(MyProduct1, 1);
    MyDict.Add(MyProduct2, 2);
    MyDict.Add(MyProduct3, 3);

    APair := MyDict.ExtractPair(MyProduct1);
    Writeln(APair.Key.Name);   // <--- Error is Here.
    Writeln(IntToStr(APair.Value));

    Readln(aKey);
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.
1
You say // <--- Error is Here., but you don't say what the error actually is. Knowing that makes it easier to help when you don't have a copy of XE installed on the machine you're reading SO from at the minute.Ken White
Sorry... I have added the concrete exception error message.ferpega
It's a mystery, I can't for the life of me see a problem!David Heffernan
Do you mean all source code is right ? That would be worse... :-( Can you reproduce the problem in your Delphi IDE installation ?ferpega
If I add this line: APair := TPair<TProduct, Integer>.Create(MyProduct3, 0); before the ExtractPair sentence, then I have no excepcion but also I have no MyProduct1 data inside the APair var.ferpega

1 Answers

11
votes

This is a Delphi bug. TDictionary<TKey,TValue>.ExtractPair does not assign Result.

RRUZ located the bug in QC.

The code reads:

function TDictionary<TKey,TValue>.ExtractPair(const Key: TKey): TPair<TKey,TValue>;
var
  hc, index: Integer;
begin
  hc := Hash(Key);
  index := GetBucketIndex(Key, hc);
  if index < 0 then
    Exit(TPair<TKey,TValue>.Create(Key, Default(TValue)));

  DoRemove(Key, hc, cnExtracted);
end;

Result should be assigned when the call to DoRemove is made.

It's quite hard to work around this bug. ExtractPair is the only way to get an item out of the dictionary without destroying the key and so you have to call it. But since it won't return the extracted item, you need to first read the item, remember the value, and then call ExtractPair.