I have object questions from classes.pas file

from classes.pas file - it contains some of the stuff below that I have never understood so does any one have experience please

TThreadList = class
private
FList: TList;
FLock: TRTLCriticalSection;
public
constructor Create;
destructor Destroy; override;

TThreadList = class has no TObject descendant so how does it exist?

IDesignerNotify = interface
[‘{B971E807-E3A6-11D1-AAB1-00C04FB16FBC}’]
procedure Modified;
procedure Notification(AnObject: TPersistent; Operation: TOperation);
end;

interface is in many places in Delphi Code and a registry GUID is for what? is it making a record with the windows registry? what’s its purpose?

TComponentClass = class of TComponent;

TComponentClass I believe is doing something in Delphi to make many TComponents in a form or something like this, am I correct?

MyClass = class 
end;

is the same meaning as

MyClass = class(TObject) 
end;

I have opined a bit about this, quite a few times this year.

Calling this Txyz makes it seem like this is a **Class** , but the Class of a Class is a MetaClass.


So what is a MetaClass?

If you imagine the type INTEGER as a box, it can hold things like : { 0, 3, -5, 99, 12, 100, 32565, … }

If you imagine the type STRING as a box, it can hold things like : { ‘a’, ‘bcdef’, ‘Hello’, ‘elephant’, ‘flugelhorn’, … }

If you imagine the type TCOMPONENT as a box,

  • it can hold Components like : { FileActionList, MyMainMenu, FontDialog1, … }
  • and Controls like : { StartButton, Edit23, MainForm, … }

The things inside all these boxes are VALUES.

The things you can put into MetaClasses are TYPES.

If you imagine the type TCOMPONENTCLASS as a box,

  • it can hold TComponent Types like : { TActionList, TMainMenu, TFontDialog, … }
  • and TControl Types like : { TPanel, TButton, TEdit, TForm, … }

MyClass = class is the same result in the compiler as MyClass = class(TObject) I can dig that. So why to they not stay to the one format. To me the complier class statement is telling the complier to allocate a following group of data in memory and then to call a descendant locks the data in place so its not externally interfered with.
I was thinking this as a type of box in my own way. or record that can be repeated with a single set of functions/procedures added

MetaClass - is a box or space that in formation in the space is managed in a unique way to its own. So where is TCOMPONENTCLASS used in code and how is it used and for what purpose can one use it for?
example I have been making a type of windows registry that has keys and they record their parent and they also use TList to record their children. I could write in TKeyClass = class of Tkey and what is that doing for me. my TKey works perfectly fine without it at the moment as far as I can see but this must be done for a reason I’m not seeing yet?
my TKey dose not have descendants where TCOMPONENT does

am I making sence?

Hi Lex,

Here’s my brief explanations.

Classes & Objects

An analogy for a class is a house design/plans and for objects the actual physical houses. The plans (class) just describe what properties the house has (dimensions, colours, materials, …) and inherent in that the operation (the garage door starts in the closed state, when the Brivis heater is turned on the element heats up when the temp is low and stops when it reaches the set temp, etc…). With the plans (class) only - without a physical house (object) nothing is happening. You can build many houses (objects) from the same plans (class). Each house (object) is independent and has features that can be in different states (garage door open/closed, heaters on/off and set to different temps, …) because it has its own values for each variable declared in the class.

So the class doesn’t tell the compiler to do anything (I’m ignoring class variables and class constructors - different to instance variables and regular constructors) - it just has details about how much memory to allocate if you were to create an object, the memory offsets and types of the variables, and the executable instructions for the procedures and functions and their memory addresses. At the point where you create an object from the class the compiler generates code to allocate the memory to store the variables for that instance, does some initialisation, calls the constructor, and saves the address of that memory to the variable to specified. When you call a method on a specific object it operates on that instance only (the procedures/functions know which set of variables to use for that object instance). The other objects are not affected.

You might create a class for the heater, and through composition define a variable of type heater in the house class. When the house object is created you create the heater object (e.g. in the house constructor). In that way each heater within each house is an independent object (as above - different on/off/set temp etc). You might also use that heater class elsewhere, like within a car class.

Interfaces

These are a bit like a blueprint of the procedures, functions and properties (but not variables - properties must use get/set methods) for classes, and can be shared across different classes. Using them with your own classes is optional. A simple analogy would be a temperature control interface with methods like TurnOn/TurnOff/SetTemperature. The heater class could implement that interface (which means the class defines the methods specified in the interface and implements their behaviour specific to the heater):

ITempControl = interface
  // There is no implementation of these
  procedure TurnOn;
  procedure TurnOff;
  procedure SetTemp(const Temp: Double);
end;

THeater = class(TObject, ITempControl)
public
  // This class implements these methods
  procedure TurnOn;
  procedure TurnOff;
  procedure SetTemp(const Temp: Double);
end;

You could also create a TAirCon class that implements the same temperature control interface, with different behaviour (maybe the aircon has a start-up period before the fan turns on). Then you could control the heater or aircon objects using the methods defined through the interface. e.g. Have a generic temperature control UI with an on/off checkbox and a set temp edit box and button. You pass the temp control interface of either the heater or the aircon object to the form - it doesn’t know anything about heaters or aircons, only the temp control interface. The user can turn the device on and set the temp and the form just calls the methods of the interface.

var
  device: ITempControl;
  tempForm: TTempControlForm;
begin
  device := THeater.Create; // or TAirCon.Create
  tempForm := TTempControlForm.Create(device);

Just like you can omit the ancestor TObject when defining the class, you can omit the IInterface ancestor when defining an interface, or specify it explicitly like IDesignerNotify = interface(IInterface).

Interfaces feature reference counting. This can be used to automatically free an object instance when the interface variable goes out of scope. Instead of declaring a variabe using the class type you declare it using (one of) the interface type that the class implements. The THeater class can descend from TInterfacedObject instead of TObject to get automatic free, and implement ITempControl. You create the object in the same way but the instance must be assigned to an interface variable. When that variable goes out of scope (e.g. the procedure that it is defined within returns) the object is automatically freed - you don’t call variable.Free.

var
  device: ITempControl;
  heater: THeater;
begin
  device := THeater.Create;
  heater := THeater.Create;

  // The heater object must be manually freed:
  heater.Free;
  // When device goes out of scope (function returns) its THeater object is auto freed

The GUID uniquely identifies the interface and is only required if you use COM interop, which is unlikely.

Class Type

TComponentClass = class of TComponent defines an ancestor class type. It is all classes of type TComponent and all descendant classes of TComponent. Normally when you create an object you specify the class type, such as TButton in the code button := TButton.Create. There are cases where the code creating the object doesn’t know the class to create. Because TButton and other controls descend from TComponent (indirectly), you can do something like this:

var
  componentClass: TComponentClass;
  component: TComponent;
begin
  if <something> then
    componentClass := TButton
  else
    componentClass := TCheckbox;

  // Later - we can create any TComponent
  component := componentClass.Create(...);

In a real app that componentClass.Create() is likely in some other part of your code that doesn’t need to know the logic behind which component needs to be created, it’s just a generic component factory. This helps to separate the rules around which object to create from the actual creation (which in this case could be general rules about associating the component with a form, default params, loading/saving the property values, …).

I hope that helps.

2 Likes

This I have to read a number of times but its the answer I was asking for and is very good thanks give me time to digest it thanks
Lex

I’m totally at home with basic objects and their descendants
now your job is to correct me on every thing and show guidance if you can.
Interfaces are very new to me. TComponentClass = class of TComponent is in the classes file that a child TComponent links into to be a object.
Let’s jump in the deep deep end!!!

question 1
TComponent’s TComponentStateItem has a few states
TComponentStateItem = (csLoading, csReading, csWriting, csDestroying,
csDesigning, csAncestor, csUpdating, csFixups, csFreeNotification,
csInline, csDesignInstance);

Public
property ComponentState: TComponentState read FComponentState;
ComponentState is in Public so accessible at run time. And a descendant example is TEdit to talk about,

I want to override ComponentState to csDesigning in run time so it can be moved in the form at run time. I can see my override code needs to test if the application is first in a run time state first and then override it to csDesigning only in that runtime state and when selected by the mouse when I need to move the screen object with the mouse. So a number of screen objects on the form can use this feature.
a) a descendant example TEdit descendant could override property ComponentState
b) an interface can add a overriding property but it still needs a descendant to include the interface so what have I gained? the rewriting of the interface code with another TComponent descendant.
c) can TComponentClass give me some access to ComponentState some how and for many TComponents.
Can you explain what I’m missing or forgot and even the pros and cons of A, B, and C

question 2
you said - The GUID uniquely identifies the interface and is only required if you use COM interop, which is unlikely.
the classes file has some examples but its not where i need to be

  1. I think that you’re heading down the wrong path with this. If you need your application to support runtime form design then your best bet is to use one of the existing runtime form designer components/libraries. Geoff mentioned this previously: Movable screen objects in run mode in a form - #2 by Geoff

  2. You are unlikely to be using COM (does anyone still use COM :smiley:) so you can ignore the GUIDs

Movable screen objects in run mode in a form - #2 by Geoff
is a bit of another subject and this is not versatile enough over enough objects.

What I’m most interested in is how to use ‘interface’ the pros and cons to the other options in Delphi to improve my own skills. ComponentState deep in the objects as the example I used

Maybe I misread the point … you can pretty easily enable dragging things around on a form.

There’s a substantial library on github :

But as a very rough, simple, example …

Looking at the Vcl.Grids.pas file the TStringGrid header is

  TStringGrid = class(TDrawGrid)
  private
    FUpdating: Boolean;
    FNeedsUpdating: Boolean;
    FEditUpdate: Integer;
    FData: TCustomData;
    FRows: TCustomData;
    FCols: TCustomData;
{$IF DEFINED(CLR)}
    FTempFrom: Integer;
    FTempTo: Integer;
{$ENDIF}
    class constructor Create;
    class destructor Destroy;
    procedure DisableEditUpdate;
    procedure EnableEditUpdate;
    procedure Initialize;
    procedure Update(ACol, ARow: Integer); reintroduce;
    procedure SetUpdateState(Updating: Boolean);
    function GetCells(ACol, ARow: Integer): string;
    function GetCols(Index: Integer): TStrings;
    function GetObjects(ACol, ARow: Integer): TObject;
    function GetRows(Index: Integer): TStrings;
    procedure SetCells(ACol, ARow: Integer; const Value: string);
    procedure SetCols(Index: Integer; Value: TStrings);
    procedure SetObjects(ACol, ARow: Integer; Value: TObject);
    procedure SetRows(Index: Integer; Value: TStrings);
    function EnsureColRow(Index: Integer; IsCol: Boolean): TStringGridStrings;
    function EnsureDataRow(ARow: Integer): TCustomData;
  protected
    procedure ColumnMoved(FromIndex, ToIndex: Longint); override;
    procedure DrawCell(ACol, ARow: Longint; ARect: TRect;
      AState: TGridDrawState); override;
    function GetEditText(ACol, ARow: Longint): string; override;
    procedure SetEditText(ACol, ARow: Longint; const Value: string); override;
    procedure RowMoved(FromIndex, ToIndex: Longint); override;
{$IF DEFINED(CLR)}
    function MoveColData(Index: Integer; ARow: TObject): Integer;
{$ENDIF}
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    property Cells[ACol, ARow: Integer]: string read GetCells write SetCells;
    property Cols[Index: Integer]: TStrings read GetCols write SetCols;
    property Objects[ACol, ARow: Integer]: TObject read GetObjects write SetObjects;
    property Rows[Index: Integer]: TStrings read GetRows write SetRows;
  end;

the TStringGrid has

    class constructor Create;
    class destructor Destroy;
..............................................

class constructor TStringGrid.Create;
begin
  TCustomStyleEngine.RegisterStyleHook(TStringGrid, TScrollingStyleHook);
end;

class destructor TStringGrid.Destroy;
begin
  TCustomStyleEngine.UnRegisterStyleHook(TStringGrid, TScrollingStyleHook);
end;

Can some one explain as TScrollingStyleHook something to do with the scroll bar movement that is in Delphi 7 on or something?
its this ‘class destructor’ that is totally new to me!!!

Check the main help file; topic “Class Method”

1 Like