views:

879

answers:

4

I'm checking out the Delphi 2009 Trial, but run into problems with the generics stuff right away.

The following code does not compile, and I haven't the slightest idea why it's giving me E2015 for the Equals() method:

type
  TPrimaryKey<T> = class(TObject)
  strict private
    fValue: T;
  public
    constructor Create(AValue: T);
    function Equals(Obj: TObject): boolean; override;
    function GetValue: T;
  end;

constructor TPrimaryKey<T>.Create(AValue: T);
begin
  inherited Create;
  fValue := AValue;
end;

function TPrimaryKey<T>.Equals(Obj: TObject): boolean;
begin
  Result := (Obj <> nil) and (Obj is TPrimaryKey<T>)
    and (TPrimaryKey<T>(Obj).GetValue = fValue);
end;

function TPrimaryKey<T>.GetValue: T;
begin
  Result := fValue;
end;

Why does the compiler think that fValue and the result of GetValue() can not be compared?

+3  A: 

You can't use operators with untyped generics. See here for a discussion.

It compiles if you change it to:

TPrimaryKey<T: class> = class(TObject)
Angus Glashier
Thanks for the link, the following opened my eyes:"Generics are compile before they get specialized with thefinal type"I thinking in terms of C++ templates is just wrong, and Delphi generics aren't a solution to my problem.
mghie
Typo correction: I _guess_ thinking ...
mghie
+1  A: 

I think the original poster is trying to create an object wrapper around simple types (Integer, double etc etc), so constraining T to Class would perhaps not work for what he wants.

Steve
Indeed. But it looks like generics as implemented in Delphi 2009 are not what I hoped they would be.
mghie
The problem is that, unlike .net, in delphi, simple types are not objects.
Steve
+1  A: 

The compiler has trouble in determining that both "T"'s are the same. But with a little trick you can make it work:

type
  TPrimaryKey<T> = class(TObject)
  public
    type
      TCompare<T1> = reference to function(const A1, A2: TPrimaryKey<T1>): Boolean;
  private
    fValue: T;
    fCompare : TCompare<T>;
  public
    constructor Create(AValue: T; ACompare: TCompare<T>);
    function Equals(Obj: TPrimaryKey<T>): Boolean; reintroduce;
    function GetValue: T;
    function CreateNew(const AValue: T): TPrimaryKey<T>;

  end;

constructor TPrimaryKey<T>.Create(AValue: T; ACompare: TCompare<T>);
begin
  inherited Create;
  fValue := AValue;
  fCompare := ACompare;
end;

function TPrimaryKey<T>.Equals(Obj: TPrimaryKey<T>): Boolean;
begin
  Result := FCompare(self, Obj);
end;

function TPrimaryKey<T>.GetValue: T;
begin
  Result := fValue;
end;

function TPrimaryKey<T>.CreateNew(const AValue: T): TPrimaryKey<T>;
begin
  Result := TPrimaryKey<T>.Create(AValue, FCompare);
end;

You instantiate it with:

var
  p1, p2 : TPrimaryKey<Integer>;
begin
  p1 := TPrimaryKey<Integer>.Create(10,
    function(const A1, A2: TPrimaryKey<Integer>): Boolean
    begin
      Result := (A1<>nil) and (A2<>nil) and (A1.GetValue=A2.GetValue);
    end);
  p2 := p1.CreateNew(10);

  p1.Equals(p2);
end;
Gamecat
+5  A: 

What if T is a string? What if it's a TSize record?

Without constraining T (e.g. with <T :class>), you can't be sure that the comparison will be meaningful.

If, instead, you wanted to compare two values of type T, you can use the Generics.Defaults unit and use:

TEqualityComparer<T>.Default.Equals(x, y)

to compare values x and y of type T.

Barry Kelly
I don't understand your first sentence (I believe the code doesn't do that), but replacing the equality check in Equals() with your TEqualityComparer code solves my problem, so thanks.Unfortunately the documentation and samples are - again - a complete let-down in Delphi 2009 :-(
mghie