Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations derfloh on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Class Variables 3

Status
Not open for further replies.

Stretchwickster

Programmer
Apr 30, 2001
1,746
GB
Hi Everyone,

Does Delphi allow you to declare "class variables", that is variables which act as metadata for a class? For example, if you had a class called TPerson, would it be possible to have a class variable called PersonCount which is incremented in the TPerson constructor. PersonCount would not be instantiated with each instance of that class but would be associated with the class itself.

I know this is possible in Java and C++ (see "Static members" section of C++ Tutorial), as I vaguely remember doing it in the past. In Java, you can achieve it by declaring a variable as static e.g.
Code:
  static int PersonCount = 0;

So is this possible in Delphi? If not, what other available option are there? My actual requirement is to have a class variable which is a collection storing information about object instances, but my simple example above illustrates the concept.

Clive [infinity]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
"To err is human, but to really foul things up you need a computer."
Paul Ehrlich
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To get the best answers from this forum see: faq102-5096
 
Hi Clive,

I don't think this is possible,
but why don't you make a new class that contains the metadata? (like TMetaClass or something like that). Make the object creators inside your metaclass, this way you can increment the reference counters for your various classes...

--------------------------------------
What You See Is What You Get
 
Cheers for the reply whosrdaddy.

I knocked up a small working example based on what you said. So that others can benefit I have outlined it below.

In one unit I declared and implemented the TPerson class and added the TMetaClass to it.
Code:
interface

type
  TPerson = class
  public
    Name: String;
    Age: Integer;
    Height: Single;
    constructor Create(AName: String; AAge: Integer; AHeight: Single);
  end;

  TMetaClass = class
  public
    PersonCount: Integer;
    function CreatePerson(AName: String; AAge: Integer; AHeight: Single): TPerson;
  end;
Code:
implementation

{ TPerson }

constructor TPerson.Create(AName: String; AAge: Integer; AHeight: Single);
begin
  Self.Name := AName;
  Self.Age := AAge;
  Self.Height := AHeight;
end;

{ TMetaClass }

function TMetaClass.CreatePerson(AName: String; AAge: Integer; AHeight: Single): TPerson;
begin
  Result := TPerson.Create(AName, AAge, AHeight);
  Inc(PersonCount);
end;

In a form I declared a private TMetaClass instance
Code:
Metadata: TMetaClass;
and instantiated TPerson objects using the following code:
Code:
procedure TForm1.FormCreate(Sender: TObject);
begin
  Metadata := TMetaClass.Create;
  Employee1 := Metadata.CreatePerson('Clive', 22, 1.85);
  ListBox1.Items.Add(Employee1.Name);
  Employee2 := Metadata.CreatePerson('Dave', 26, 1.65);
  ListBox1.Items.Add(Employee2.Name);
  Employee3 := Metadata.CreatePerson('Fabian', 39, 1.87);
  ListBox1.Items.Add(Employee3.Name);
  LabeledEdit1.Text := IntToStr(metadata.PersonCount);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  Employee1.Free;
  Employee2.Free;
  Employee3.Free;
  Metadata.Free;
end;


Clive [infinity]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
"To err is human, but to really foul things up you need a computer."
Paul Ehrlich
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To get the best answers from this forum see: faq102-5096
 
It is not necessary to create a "Meta" object. You can use the TObjectList to accomplish your purpose. (The .Count property provides your "PersonCount" number.)

For example:
Code:
  TDivisionData = class(TObject)
  private
    FID:string;
    FKey:integer;
    FStatus:string;
  public
    constructor Create( ID:string;
                   Key:integer; Status:string );
    property ID:string     read FID;
    property Key:integer   read FKey;
    property Status:string read FStatus;
  end;

  TfrmMain = class(TForm)
    pbProcess: TButton;
    qLocal:TADOQuery;
  private
    CurrentDivisionKeys: TObjectList;
  end;

constructor TDivisionData.Create(ID: string;
                  Key: integer; Status: string);
begin
  FID := ID;
  FKey := Key;
  FStatus := Status;
end;

procedure TfrmMain.PopulateDivisionData(Account: integer);
begin
    CurrentDivisionKeys.Clear;
    with qLocal do begin
      Close;
      SQL.Clear;
      SQL.Add('SELECT divisionid,divisionkey,divisionstatus ');
      SQL.Add(' FROM DIVISIONS ');
      SQL.Add(' WHERE accountkey=' + IntToStr(Account) );
      Open;
      while not Eof do begin
        CurrentDivisionKeys.Add(
           TDivisionData.Create(
                Fields[0].AsString,
                Fields[1].AsInteger,
                Fields[2].AsString) );
        Next;
      end; //while
      Close;
    end; //with
end;
The TObjectList automatically frees memory for the objects it owns (Unless you set the .OwnsObjects property to False) so all you need to do on form destroy is free the TObjectList.

When I need random access to the objects, I use a TStringList instead. (A TStringList has an .Objects property along with an .AddObject method.) I put the key value as the string. I can then use the .IndexOf method for random access. The only glitch is that I have to free the individual objects before freeing the TStringList -- it is not automatic as with TObjectList.
 
This could be just me of course, but why not use an array?
You got all the functionality that you need (apperantly) right there.

[bobafett] BobbaFet [bobafett]

Everyone has a right to my opinion.
E-mail me at caswegkamp@hotmail.com
 
I think what Stretchwikster means is that you can have a class with contains data about other classes. so for instance an objectlist is nice but doesn't contain really detailed data about the classes it owns. It all depends on what you really want to do...

--------------------------------------
What You See Is What You Get
 
You're right, of course. But the example posed by Clive shows that he only wants to keep track of the count. This is automatic with a TObjectList.

Obviously, if further metadata are required, a custom object can be used, and probably should be. I would suppose however that the "TMetaData" would either be a descendent of TObjectList or have a TObjectList as one of its properties. This comes under the category of "don't reinvent the wheel."

 
Zathras Quote: "don't reinvent the wheel."

My point exactly, so why not use an array.

[bobafett] BobbaFet [bobafett]

Everyone has a right to my opinion.
E-mail me at caswegkamp@hotmail.com
 
don't see the need of arrays when you can have objectlists...

--------------------------------------
What You See Is What You Get
 
The way I implement "class variables" in Delphi is to define a class in its own source code unit and declare the class variable(s) in the implementation part.

Using Clive's TPerson as an example my code would look something like:
Code:
unit uPerson;   // I prefix unit names with 'u'

interface

type
  TPerson = class
  private
    function GetPersonCount: integer;
  public
    Name: String;
    Age: Integer;
    Height: Single;
    constructor Create(AName: String; AAge: Integer; AHeight: Single);
    destructor Destroy; override;
    property PersonCount: integer read GetPersonCount;
  end;

implementation

{ TPerson }

var   // Class Variables
  cvPersonCount: integer = 0;

constructor TPerson.Create(AName: String; AAge: Integer; AHeight: Single);
begin
  Self.Name := AName;
  Self.Age := AAge;
  Self.Height := AHeight;
  inc ( cvPersonCount );
end;

destructor TPerson.Destroy;
begin
  Dec ( cvPersonCount );
end;

function TPerson.GetPersonCount: integer;
begin
  result := cvPersonCount;
end;

end.
This is not quite as neat as the java or C++ way of handling class variables but in practice it works. The important thing is to have a separate unit for each class so that the "class variables" are encapsulated in the unit.

In the real world there would be several class variables and not just a simple count of the number of created objects.



Andrew
Hampshire, UK
 
Sorry guys - I've caused confusion by following through my simple TPerson example. I was just trying out some concepts with this class and simple count.

However, as I first stated:
My actual requirement is to have a class variable which is a collection storing information about object instances, but my simple example above illustrates the concept.

I wanted a class variable of type TStSortedCollection in which to insert records (of object instance information). In the end I went for an extension of Andrew's idea - mainly because my boss wanted me to use TStSortedCollection objects. I added a collection variable to the unit, created it in the Initialization section and destroyed it in the Finalization section and it seems to work nicely.

whosrdaddy, thanks for your initial idea of a custom class - I think it is of benefit for storing detailed instance information.

Zathras, thank you very much for introducing me to TObjectList components - surprisingly, I've not come across them before in the 2 years I've been using Delphi. I also found TObjectStack and TObjectQueue - all of which may be useful in future projects.

Andrew, your example is very neat - I am impressed. I think that's a good stab at mimicking Java and C++ behaviour.

I feel this discussion has been extremely useful in exploring the concept of class variables in Delphi. Apparently, Delphi 8 implements "class fields" using the following syntax
Code:
class var PersonCount: integer;
Unfortunately I am using Delphi 6 though!

Thanks again for all of your input!

Clive [infinity]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
"To err is human, but to really foul things up you need a computer."
Paul Ehrlich
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To get the best answers from this forum see: faq102-5096
 
Well, the reason I prefer multi-dimensional dynamic arrays is because they are allot simpler to implement and your code doesn't look that messed up, which I feel is a result of meddling about with custom classes, arrays are allot easier to read then custom classes I think. Create a bunch of functions around them and you are good to go.

This usually results into 4 procedures and (in this example of the person information) 4 private declarations in the form. One procedure to add information, One to increase or decrease the size of the array by 1 (boolean var determines what to do), which is called from the adding procedure, one to extract information, which goes to the globally declared variables and one to delete information, which copies the next set over the previous one and at the end the new size is set. Which gives you the gift of not having to free up all those instances.

I personally really don't like to mess around with classes because it seems so unco-ordinated to me, looks like loose pieces of information all over the place, with an array you got it all nicely packed together in one place. So personally I really do not see the need for custom classes when you can have a multi-dimensional dynamic array.

It's just a matter of personal taste I guess and the fact that I taught myself to program probably doesn't help either when you consider "standard procedures" within programming hahaha. But anyway this is the reason for me.

[bobafett] BobbaFet [bobafett]

Everyone has a right to my opinion.
E-mail me at caswegkamp@hotmail.com
 
So basically what I'm trying to say is instead of having (sticking to the example) Person here, Person there, Person this and Person that, you got one group of Persons each with their own specifications.

[bobafett] BobbaFet [bobafett]

Everyone has a right to my opinion.
E-mail me at caswegkamp@hotmail.com
 
And another thing which I forgot to mention is the ObjectList so say that you got all those TPersons, you look em up in the objectlist and then you got to enter another object to get the information, with the way I do it you just loop through the array or enter the index number and you got everything you need. Allot less meddling about with objects.

[bobafett] BobbaFet [bobafett]

Everyone has a right to my opinion.
E-mail me at caswegkamp@hotmail.com
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top