Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Need to associate unique integer value with classes

Alright, so I have a base class which we'll call TFruit. From this there are various descendants like TApple, TOrange and so on. I need to save the properties of the descendant classes to a file.

In order to be able to create the right class when loading the data, each class needs to have an ID that I write to the file before writing the actual data. Currently, I've come up with the following way of doing it:

type
  TFruit = class
    const ID = 0;
  end;

  TApple = class(TFruit)
    const ID = 1;
  end;

  TOrange = class(TFruit)
    const ID = 2;
  end;

Testing this, I found out that I need to be super careful which class I declare. If I use this:

  var Fruit: TFruit;

  Fruit := TOrange.Create;

...then Fruit.ID will return zero. However, declaring Fruit as a TOrange will yield the expected result Fruit.ID = 2 (anyone know why?)

So basically, am I doing this right or is there a better way to do it? Having to create a class function and return a value from there seems very ugly by comparison (extra function declaration, implementation and code).

like image 417
David Avatar asked Nov 30 '10 14:11

David


2 Answers

An easier to maintain solution would be to create a mapping class where you register all classes you'd like to convert to an integer.

Advantages

  • Ability to detect duplicate registrations.
  • Independent of your class structure.
  • Includes the transformation back to a classname.

Usage

  RegisterClass.Register(0, TFruit);
  RegisterClass.Register(1, TApple);
  RegisterClass.Register(2, TOrange);

Implementation

  TRegisterClass = class
  private
    FList: TStringList;
  public
    function FindID(AClass: TClass): Integer;
    function FindClassName(const ID: Integer): string;
    procedure Register(const ID: Integer; AClass: TClass);
  end;
  ...
  function TRegisterClass.FindID(AClass: TClass): Integer;
  begin
    Assert(Assigned(AClass));

    Result := -1;
    if FList.IndexOf(AClass.ClassName) <> -1 then
      Result := Integer(FList.Objects[FList.IndexOf(AClass.ClassName)]);
  end;

  function TRegisterClass.FindClassName(const ID: Integer): string;
  var
    I: Integer;
  begin
    Result := EmptyStr;
    for I := 0 to Pred(FList.Count) do
      if Integer(FList.Objects[I]) = ID then
      begin
        Result := FList[I];
        Exit;
      end;
  end;

  procedure TRegisterClass.Register(const ID: Integer; AClass: TClass);
  begin
    if IsAlreadyRegistered(ID) then 
      raise Exception.Create('Duplicate ID Registration')
    else if IsAlreadyRegistered(AClass) then 
      raise Exception.Create('Duplicate Class Registration');

    FList.AddObject(AClass.ClassName, Pointer(ID)); 
  end;

Please note that there are better structures to map a String to an Integer. Writing this without a compiler and don't knowing many basic structures beyond Delphi5, I've chosen an obvious implementation.

Note that the IsAlreadyRegistered overloaded functions still have to be written

like image 178
Lieven Keersmaekers Avatar answered Oct 12 '22 19:10

Lieven Keersmaekers


there are many possibilities, for example:

function TFruit.GetClassId(): Word;
begin
  Result := CRC16(ClassName);
end;
like image 36
Free Consulting Avatar answered Oct 12 '22 20:10

Free Consulting