Why use property in a class? - delphi

I was just wondering about why should I use property in a class instead of "normal" variables (class attributes?). What I mean is this:
TSampleClass = class
public
SomeInfo: integer;
end;
TPropertyClass = class
private
fSomeInfo: integer;
public
property SomeInfo: integer read fSomeInfo write fSomeInfo;
end;
What is the big difference? I know that I can define getter and setter methods for getting or saving the property respectively, but that is possible even without the variable being a "property".
I tried searching for why to use it, but nothing useful came up, so I'm asking here.
Thank you

This is just a very simple example of a specific case, but still, it is a very common case.
If you have a visual control, you might need to repaint the control when you change a variable/property. For instance, let's say your control has a BackgroundColor variable/property.
The simplest way of adding such a variable/property is to let it be a public variable:
TMyControl = class(TCustomControl)
public
BackgroundColor: TColor;
...
end;
And in the TMyControl.Paint procedure, you paint the background using the value of the BackgroundColor. But this doesn't do it. Because if you change the BackgroundColor variable of an instance of the control, the control doesn't repaint itself. Instead, the new background colour will not be used until the next time the control redraws itself for some other reason.
So you have to do it like this:
TMyControl = class(TCustomControl)
private
FBackgroundColor: TColor;
public
function GetBackgroundColor: TColor;
procedure SetBackgroundColor(NewColor: TColor);
...
end;
where
function TMyControl.GetBackgroundColor: TColor;
begin
result := FBackgroundColor;
end;
procedure TMyControl.SetBackgroundColor(NewColor: TColor);
begin
if FBackgroundColor <> NewColor then
begin
FBackgroundColor := NewColor;
Invalidate;
end;
end;
and then the programmer using the control has to use MyControl1.GetBackgroundColor to obtain the colour, and to use MyControl1.SetBackgroundColor to set it. That's awkward.
Using properties, you can have the best of both worlds. Indeed, if you do
TMyControl = class(TCustomControl)
private
FBackgroundColor: TColor;
procedure SetBackgroundColor(NewColor: TColor);
published
property BackgroundColor: TColor read FBackgroundColor write SetBackgroundColor;
end;
...
procedure TMyControl.SetBackgroundColor(NewColor: TColor);
begin
if FBackgroundColor <> NewColor then
begin
FBackgroundColor := NewColor;
Invalidate;
end;
end;
then
from the programmer's point of view, he can both read and set the background colour using a single identifier, the MyControl1.BackgroundColor property, and
the control is repainted when he sets it!

There are real-life advantages:
Properties can be changed to be read/write/read'n'write easily, without need to hassle with separate Getters and Setters all over the code;
Properties can be made public/published in child classes by just adding one line in initialization section;
Properties are more friendly when it comes to setting fields, compare "Label.Font.SetSize(14)" with "Label.Font.Size := 14", you can align ":=" with tabs/spaces and code will be much more readable;
EDIT: Another thing I thought of, properties force you to limit Get/Set methods to only 1 parameter, which is good for OOP. Compare that to some over-engineered functions:
GetItem(Index:integer; ForcedIndex:boolean=false):TItem //Forced index to get any value
GetItem(Index:integer; out Res:PItem):boolean //Result signals if out pointer is valid

I know that I can define getter and setter methods for getting or saving the property respectively, but that is possible even without the variable being a "property".
Well, no. Setters and getters are just normal methods that are called as such only once they are used as the read and write members of a property. Not having a property means not having a getter or a setter, even if they are named as such.
Furthermore; setters and getters are typically declared private or protected. So being able to call them when you use a public field instead of using a public property would require to move those methods to the public section.
Also, a big difference between fields and properties is the ability to be published and thus can be used in the object inspector. Fields (of other types then class or interface) can not be declared as published.
Properties can also be of great importance - or be usefull - in inheritance. Technically, you can't override a property, but you can mimic override in several ways. Some examples where property Name can be called from TDescendant, each with its own purpose:
1) Abstraction:
TBase = class(TObject)
protected
function GetName: String; virtual; abstract;
procedure SetName(const Value: String); virtual; abstract;
public
property Name: String read GetName write SetName;
end;
TDescendant = class(TBase)
private
FName: String;
protected
function GetName: String; override;
procedure SetName(const Value: String); override;
end;
2a) Protection (like Krom mentioned, ):
TBase = class(TObject)
private
FName: String;
function GetName: String;
procedure SetName(const Value: String);
protected
property Name: String read GetName write SetName;
end;
TDescendant = class(TBase)
public
property Name;
end;
2b)
TBase = class(TObject)
private
FName: String;
protected
function GetName: String;
procedure SetName(const Value: String);
end;
TDescendant = class(TBase)
public
property Name: String read GetName write SetName;
end;
By combinination of the above, you could change the behaviour of properties for descendant classes.

It is just a good programming practice to isolate the very "innards" of your class from the outside world. In addition, information about published properties are stored into RTTI generated for the class and can be accessed by their name, enumerated etc. This feature is used for example when reading a form from its serialized resource form.

One of main reason of using properties (regardless of it's more OO) is the validation of the input, for example if you need to limit the age of an employee class to be in valid range like 18..40
TEmp = class
private
FName: string;
FAge: Integer;
procedure SetAge(const Value: Integer);
procedure SetName(const Value: string);
published
property Name:string read FName write SetName;
property Age:Integer read FAge write SetAge;
end;
.....
procedure TEmp.SetAge(const Value: Integer);
begin
if not (Value in [18..40]) then
raise Exception.Create('Age must be between 18 and 40')
else
FAge := Value;
end;

You cant monitor the change in a variable without a property.
your read/writes for property dont have to be a variable they can be functions. And then you can manage the "onChange" of a property.
eg
TmyChange = procedure(Sender: Tobject) of object;
private
Fchange : TmyChange;
public
property SomeInfo: integer read getFoo write setFoo;
property onChange : TmyChange read Fchange write Fchange;
function getFoo : integer
begin
return localFoo;
end;
function setFoo (value : integer)
begin
// validate incoming value
localFoo=value;
if assigned(Fchange) then Fchange(self);
end;

Related

Delphi - Using a function with a property setter

Delphi RIO - have defined a class called TBizObj. One of the properties has to do with DUNS numbers. DUNS numbers are sometimes '0' padded on the left to be exactly 9 characters long, so I have a property called SiteDUNS9 (based off of fSiteDUNS9). The calling program sets the SiteDUNS9 property, but I don't the caller to have to worry about if the DUNS is 9 characters or not, I will handle that in getter/setter properties.
When I define my property to call this function, I get an error 'Incompatible types'. Everything is string... no other types involved. Here is the relevant portion of code:
type
TBizObj = class(TObject)
private
...
fSiteDUNS9: string;
...
function FixDunsLength9(DUNS:string) :string;
published
...
property SiteDUNS9: string read fSiteDUNS9 write FixDunsLength9;
end; // End of the tBizObj Class;
implementation
...
function TBizObj.FixDunsLength9(DUNS:string):string;
begin
// This is a setter function for the DUNS9 routine
result := glib_LeftPad(DUNS, 9, '0');
end;
I have followed the examples on the Embaracadero site but still cannot determine what I am doing wrong.
http://docwiki.embarcadero.com/RADStudio/Rio/en/Properties_(Delphi)
If I change my property definition to
property SiteDUNS9: string read fSiteDUNS9 write fSiteDUNS9;
then my program compiles correctly.
You need to use a procedure instead of a function for a property setter. I would leave the existing function as-is, in case you need it for other purposes, and define a separate procedure for the setter:
type
TBizObj = class(TObject)
private
...
fSiteDUNS9: string;
...
function FixDunsLength9(const DUNS: string): string;
procedure SetSiteDUNS9(const Value: string);
published
...
property SiteDUNS9: string read fSiteDUNS9 write SetSiteDUNS9;
end;
// End of the tBizObj Class;
implementation
...
function TBizObj.FixDunsLength9(const DUNS: string): string;
begin
Result := glib_LeftPad(DUNS, 9, '0');
end;
procedure TBizObj.SetSiteDUNS9(const Value: string);
var
NewValue: string;
begin
NewValue := FixDunsLength9(Value);
if fSiteDUNS9 <> NewValue then
begin
fSiteDUNS9 := NewValue;
...
end;
end;
You need to declare a procedure for the setter method. As the Property Access help says:
write fieldOrMethod
In a write specifier, if fieldOrMethod is a method, it must be a
procedure that takes a single value or const parameter of the same
type as the property (or more, if it is an array property or indexed
property).
In your case you can then write a setter like this:
type
TBizObj = class(TObject)
private
FSiteDUNS9: string;
procedure FixDunsLength9(const DUNS: string);
published
property SiteDUNS9: string read FSiteDUNS9 write FixDunsLength9;
end;
implementation
procedure TBizObj.FixDunsLength9(const DUNS: string);
begin
if DUNS <> FSiteDUNS9 then
begin
DoSomeExtraStuff;
FSiteDUNS9 := DUNS;
end;
end;
But following the naming conventions I would recommend you to name your setter like SetSiteDUNS9 and the parameter call Value.

How to let the IDE know that I use ancestor variable?

For simplicity, I only have 2 classes TParent and TChild.
TParent = class
protected
FValue : Integer;
end;
TChild = class(TParent)
public
property Value : Integer read FValue;
end;
If the TChild property Value uses the TParent variable FValue which is in another unit, the IDE always creates new variable when using auto-complete, which is a problem when adding new properties or methods and may cause unwanted errors.
TChild = class(TParent)
private
FValue: Integer;
public
property Value : Integer read FValue;
end;
However, if TParent and TChild are in the same unit, everything works fine. Is there any way to prevent this if I don't have the ability to move both classes to the same unit? Also I don't have access to a unit containing TParent. In this case, TChild is a component derived from TCustomGrid.
This is just the nature of inheritance, more specifically, field visibility. The simple solution would be to introduce a property getter function with a higher visibility. For example...
TParent = class
protected
FValue : Integer;
public
function GetValue: Integer;
end;
TChild = class(TParent)
public
property Value : Integer read GetValue;
end;
...
function TParent.GetValue: Integer;
begin
Result:= FValue;
end;
Code completion is just following these same rules - it doesn't have visibility of the parent's field, so it generates a new one.

delphi component property dynamic value

how can i get dynamic values for my component's property variable
in my component i have a field named ColorDefault and i want to be able to set its value dynamically in program.
Original code
var // global
_V_TB_DefaultColor: TColor
type
TMyClass = class
...
property ColorDefault: tcolor read _V_TB_DefaultColor write FDefaultColor;
//[dcc32 Error] MyButton.pas(85): E2168 Field or method identifier expected
...
end;
Edit:
I did as below as tom described but color is not changing according to the global variable, color stays as when it is complied, for example my global color was clyellow and i complied my component and place it on the form and after that i changed the global color variable to clwhite and when i run the program it is still clyellow
type
TTestClass = class(TPanel)
private
{ Private declarations }
protected
{ Protected declarations }
FColorDefault:tcolor;
public
{ Public declarations }
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
function GetGlobalColorVariable:TColor;
published
{ Published declarations }
property DefaultColor:TColor read GetGlobalColorVariable write FColorDefault;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('MyComponents', [TTestClass]);
end;
constructor TTestClass.Create(AOwner: TComponent);
begin
ColorInitiate;
inherited;
color:=DefaultColor;
end;
destructor TTestClass.Destroy;
begin
inherited;
end;
function TTestClass.GetGlobalColorVariable: TColor;
begin
result:=_V_TB_DefaultColor;
end;
end.
Looking at your question it seems that you are dealing with two problems.
First problem is how to access some global variable using property.
You can do this by using Getter method of your property like so:
type
TTestClass = class(TPanel)
private
{ Private declarations }
protected
{ Protected declarations }
public
{ Public declarations }
constructor Create(AOwner: TComponent); override;
function GetDefaultColor: TColor;
published
{ Published declarations }
property DefaultColor: TColor read GetDefaultColor;
end;
var // global
_V_TB_DefaultColor: TColor
implementation
function TTestClass.GetGlobalColorVariable: TColor;
begin
result := _V_TB_DefaultColor;
end;
That is like you did in your question edit. This will always make your DefaultColor property to return the same value as it is stored in your global _V_TB_DefaultColor variable.
But do note that this won't detect when _V_TB_DefaultColor variable was changed. So if you want to update your components after the change you need to execute some updating procedure for each of them yourself.
Also bare in mind that using global variables like this is not a good practice.
If you want certain property of all of your components to have the same value the it would be much better to declare that property as class property like in the code bellow.
type
TTestClass = class(TPanel)
private
{ Private declarations }
class var FDefaultColor: TColot;
protected
{ Protected declarations }
public
{ Public declarations }
constructor Create(AOwner: TComponent); override;
class function GetDefaultColor: TColor;
published
{ Published declarations }
class property DefaultColor: TColor read FDefaultColor write FDefaultColor;
end;
Now changing your DefaultColor in one component will change DefaultColor for all components of the same class. But bare in mind that you will still have to execute proper procedure for each of your components to update itself. That won't be done automatically.
Now your second problem is to detect the change of your DefaultColor variable and update your component/s accordingly.
Now if you use global variable there is no mechanism to detect this. But if you are using class procedure then you can at least write a setter method that will either execute update methods of all of your components that needs to be updated or send them necessary notification so they can perform necessary update by themselves.
How to implement this?
One way would be to loop through all your forms components checking their type and executing necessary update procedure. But that can be quite slow if you have lots of other components that you need to check if they are correct ones.
Another probably better approach would be adding your components on separate list so you don't need to be type checking as you know that such list only contains the right components.
You neeed to review the documentation about properties
http://docwiki.embarcadero.com/RADStudio/XE7/en/Properties
Your code
property ColorDefault:tcolor read _V_TB_DefaultColor write FDefaultColor;
is not approved by the compiler because _V_TB_DefaultColor is not a field or method of your class. The property should probably be declared as:
property ColorDefault:tcolor read FDefaultColor write FDefaultColor;
To set FDefaultColor equal to the global variable _V_TB_DefaultColor you need
MyClass.ColorDefault := _V_TB_DefaultColor;
at a suitable place in your code, f.ex in the constructor of your class.
Note, that to change the color of your component, you need to assign a new value to the ColorDefault property. Changing the value of your global variable _V_TB_DefaultColor will not automagically change the value of the property.
In your revised code you added
function TTestClass.GetGlobalColorVariable: TColor;
begin
result:=_V_TB_DefaultColor;
end;
That will not do anything if you dont assign the result of the function to something. Since the function is a member of TTestCalss I believe you want to
set the Color property directly in this function:
function TTestClass.GetGlobalColorVariable: TColor;
begin
Color:=_V_TB_DefaultColor;
end;
If this is the case, you can change it to a procedure because you dont use the return value:
procedure TTestClass.GetGlobalColorVariable;
begin
Color:=_V_TB_DefaultColor;
end;
thanks everyone,
with all the suggestions what i do is using a function to get the value from global variable and using windows messages as trigger to apply the new values to component

Polymorphism and properties

I'm trying to use polymorphism and create a property that should return different types depending on what type of object it is.
Why is there this compiler warning for the code below:
[Warning: Constructing a class "TTimeField" with abstract method "SetValue"]
More important, how can I make this work so Value can be assigned a TDateTime when a TTimeField is created? As it is, there is a message about "type mismatch".
TBaseField = class
private
function GetValue: string; virtual; abstract;
procedure SetValue(AValue: string); virtual; abstract;
public
property Value: string read GetValue write SetValue;
end;
{ TTimeField }
TTimeField = class(TBaseField)
private
FValue : TDateTime;
function GetValue: TDateTime; override;
procedure SetValue(AValue: TDateTime); override;
public
property Value: TDateTime read GetValue write SetValue;
end;
implementation
function TTimeField.GetValue: TDateTime;
begin
Result:= FValue;
end;
procedure TTimeField.SetValue(AValue: TDateTime);
begin
FValue:= AValue;
end;
EDIT: Below, with the use of strings I achieved the functionality needed, but if the code above had worked it had meant a gain in performance.
TBaseField = class
private
procedure SetStrValue(AValue: string); virtual; abstract;
function GetStrValue: string; virtual; abstract;
public
property AsString: string read GetStrValue write SetStrValue;
end;
TTimeField = class(TBaseField)
private
FValue : TDateTime;
function GetStrValue: string; override;
procedure SetStrValue(AValue: string); override;
public
property AsString: string read GetStrValue write SetStrValue;
end;
function TTimeField.GetStrValue: string;
begin
Result:= DateTimeToStr(FValue);
end;
procedure TTimeField.SetStrValue(AValue: string);
begin
FValue:=StrToDateTime(AValue);
end;
I think your problem is that you're not actually overriding any method; your new method has a different signature. Therefore it is actually an overload. In your example, having TBaseField doesn't really help you in any way. The subclass doesn't use any of its methods, nor does it provide any extra functionality.
Polymorphism is about varying implementations for a given interface (as in method signatures) without the caller needing to knowing about that.
Looking at your example, it might just be that you're not necessarily looking for polymorphism, but rather for a way to implement similar methods/properties for various data types with as little code as possible. That's where generics usually prove more useful.
Say that all field classes need to store a value of their own type, which should be possible to set and retrieve. Then you can define a generic TField<T> where T is a "placeholder" for the type of data that the field should work with. You will be able to give it a getter and setter, as well as a property, which will be generic too. As such, you'll only need to define them once for all kinds of fields you'll end up using. These methods may contain whatever logic is shared by all fields. You may, however, still subclass a generic type to add specific functionality to a certain type, or to hide the fact that you rely on generics.
A simple example:
type
TField<T> = class
private
FValue: T;
protected
function GetValue: T; virtual;
procedure SetValue(const AValue: T); virtual;
public
property Value: T read GetValue write SetValue;
end;
TDateTimeField = TField<TDateTime>;
TTrimmingStringField = class(TField<string>);
protected
function SetValue(const AValue: string); override;
end;
// Now use these fields as you'd expect
implementation
{ TField<T> }
function TField<T>.GetValue: T;
begin
Result := FValue;
end;
procedure TField<T>.SetValue(const AValue: T);
begin
FValue := AValue;
end;
{ TTrimmingStringField }
procedure TTrimmingStringField.SetValue(const AValue: string);
begin
inherited SetValue(Trim(AValue));
end;
Polymorphism is used with methods rather than properties. You can define a property that has virtual getter and setter methods, and then use polymorphism on those getter and setter methods.
However, polymorphic methods are not allowed to change the type of the arguments or the return value. Why not?
Well, suppose we write this code:
type
TMyBaseClass = class
public
procedure Foo(Arg: string); virtual;
end;
....
var
obj: TMyBaseClass;
....
obj.Foo('bar');
One of the tenets of polymorphism is that obj can be an instance of TMyBaseClass or indeed any class derived from TMyBaseClass. The compiler does not need to know the actual type of obj, only that it is derived from TMyBaseClass. The type is only known at runtime, and polymorphic virtual method despatch ensures that the derived method is called.
This means that it would make no sense for Foo to accept arguments of any type other than that declared when the virtual method was first introduced.
What you need is something called a variant type. A variant type is one that can represent many different types. For instance, any decent variant type will be able to represent integers, floating point values, strings, dates and times and so on. In Delphi you would use something like the COM Variant type, or the more recently added TValue. In Free Pascal, I'm not so sure what the prevailing variant type is.

Delphi: Using function from main class in a subclass

On the new side of writing classes and have a little problem which I have tried researching but still no answer.
I want to create one instance of a class which creates multiple subclasses which creates subclasses of their own. The idea is to use code like this in main program:
procedure TForm1.Button1Click(Sender: TObject);
var
Temp : Integer;
begin
MainClass := TMainClass.Create(Form1);
Temp := MainClass.SubClass1.SubSubClass1.SomeValue;
end;
The main class looks like this and is created in seperate file:
TMainClass = class(TObject)
private
FSubClass1 : TSubClass1;
public
ValueFromAnySubClass : Integer;
property SubClass1 : TSubClass1 read FSubClass1 write FSubClass1;
procedure SetSomeValueFromMainClass(Value : Integer);
end;
...
...
...
procedure TMainClass.SetSomeValueFromMainClass(Value : Integer);
begin
ValueFromAnySubClass := Value;
end;
The sub class also in seperate file:
TSubClass1 = class(TObject)
private
FSubSubClass1 : TSubSubClass1;
public
property SubSubClass1 : TSubSubClass1 read FSubSubClass1 write FSubSubClass1;
end;
And now for the sub sub class also in seperate file:
TSubSubClass1 = class(TObject)
private
SomeValue : Integer;
function GetSomeValue : Integer;
procedure SetSomeValue(Value : Integer);
public
property SomeValue : Integer read GetSomeValue write SetSomeValue;
end;
...
...
...
procedure TSubSubClass1.SetSomeValue(Value : Integer);
begin
SetSomeValueFromMainClass(Value); <<< Error Here <<<
end;
How do I get to use the functions and procedures from the main class in my sub classes?
You don't need a subclass to use a function from another class. Also your sample code has not used subclasses at all. A proper subclass automatically has access to all public and proteced functions of its ancestors.
As David has already pointed out, there are serious flaws in your intended deisgn.
Furthermore, based on your comment:
The classes all perform vastly different functions but need to write data to a piece of hardware at the end of the day. The data is read from the hardware and kept in memory to work with until its written back to the hardware component once all work is completed. The procedure in the main class takes care of writing real time data to the hardware whenever it is required by any of the subclasses.
to David's answer: you don't need subclasses at all.
All you need is a public method on your hardware class. And for each instance of your other classes to have a reference to the correct instance of your hardware class.
type
THardwareDevice = class(TObject)
public
procedure WriteData(...);
end;
TOtherClass1 = class(TObject)
private
FDevice: THardwareDevice;
public
constructor Create(ADevice: THardwareDevice);
procedure DoSomething;
end;
constructor TOtherClass1.Create(ADevice: THardwareDevice);
begin
FDevice := ADevice;
end;
procedure TOtherClass1.DoSomething;
begin
//Do stuff, and maybe you need to tell the hardware to write data
FDevice.WriteData(...);
end;
//Now given the above you can get two distinct object instances to interact
//as follows. The idea can be extended to more "other class" types and instances.
begin
FPrimaryDevice := THardwareDevice.Create(...);
FObject1 := TOtherClass1.Create(FPrimaryDevice);
FObject1.DoSomething;
//NOTE: This approach allows extreme flexibility because you can easily
// reference different instances (objects) of the same hardware class.
FBackupDevice := THardwareDevice.Create(...);
FObject2 := TOtherClass1.Create(FBackupDevice);
FObject2.DoSomething;
...
end;
The design looks really poor. You surely don't want to have all these classes knowing all about each other.
And any time you see a line of code with more than one . operator you should ask yourself if the code is in the right class. Usually that indicates that the line of code that has multiple uses of . should be in one of the classes further down the chain.
However, if you want to call a method, you need an instance. You write:
procedure TSubSubClass1.SetSomeValue(Value : Integer);
begin
SetSomeValueFromMainClass(Value);
end;
And naturally this does not compile. Because SetSomeValueFromMainClass is not a method of TSubSubClass1. Rather SetSomeValueFromMainClass is a method of TMainClass. So, to call that method, you need an instance of TMainClass.
Which suggests that, if you really must do this, that you need to supply to each instance of TSubSubClass1 an instance of TMainClass. You might supply that in the constructor and make a note of the reference.
Of course, when you do this you now find that your classes are all coupled together with each other. At which point one might wonder whether or not they should be merged.
I'm not saying that merging these classes is the right design. I would not like to make any confident statement as to what the right design is. Perhaps what you need is an interface that promises to implement the setter as a means to decouple things. All I am really confident in saying is that your current design is not the right design.
As far as I know Subclass word is usually using in inheritance concept but the code you wrote are some compound classes. As you may see the constructor of many classes in Delphi have an argument which named AOwner that may be TComponent or TObject or ...
If you define the constructors of your TSubclass1 and TSubSubClass1 like as follow and Change the Owner of classes that you defined as properties to Self in set functions you may access to your TMainClass by typecasting the Owner property.
I changed your code a little to just work as you want, but I suggest change your design.
TSubSubClass1 = class(TObject)
private
FOwner: TObject;
function GetSomeValue:Integer;
procedure SetSomeValue(const Value: Integer);
procedure SetOwner(const Value: TObject);
public
constructor Create(AOwner:TObject);reintroduce;
property Owner:TObject read FOwner write SetOwner;
property SomeValue : Integer read GetSomeValue write SetSomeValue;
end;
TSubClass1 = class(TObject)
private
FSubSubClass1: TSubSubClass1;
FOwner:TObject;
procedure SetSubSubClass1(const Value: TSubSubClass1);
procedure SetOwner(const Value: TObject);
public
constructor Create(AOwner:TObject);reintroduce;
property Owner:TObject read FOwner write SetOwner;
property SubSubClass1 : TSubSubClass1 read FSubSubClass1 write SetSubSubClass1;
end;
TMainClass = class(TObject)
private
FSubClass1: TSubClass1;
procedure SetSubClass1(const Value: TSubClass1);
public
ValueFromAnySubClass : Integer;
constructor Create;
property SubClass1 : TSubClass1 read FSubClass1 write SetSubClass1;
procedure SetSomeValueFromMainClass(Value : Integer);
end;
implementation
{ TSubSubClass1 }
constructor TSubSubClass1.Create(AOwner: TObject);
begin
Owner:=AOwner;
end;
function TSubSubClass1.GetSomeValue: Integer;
begin
Result:=TMainClass(TSubClass1(Self.Owner).Owner).ValueFromAnySubClass;
end;
procedure TSubSubClass1.SetOwner(const Value: TObject);
begin
FOwner := Value;
end;
procedure TSubSubClass1.SetSomeValue(const Value: Integer);
begin
TMainClass(TSubClass1(Self.Owner).Owner).SetSomeValueFromMainClass(Value);
end;
{ TSubClass1 }
constructor TSubClass1.Create(AOwner: TObject);
begin
Owner:=AOwner;
FSubSubClass1:=TSubSubClass1.Create(Self);
end;
procedure TSubClass1.SetOwner(const Value: TObject);
begin
FOwner := Value;
end;
procedure TSubClass1.SetSubSubClass1(const Value: TSubSubClass1);
begin
FSubSubClass1 := Value;
FSubSubClass1.Owner:=Self;
end;
{ TMainClass }
constructor TMainClass.Create;
begin
FSubClass1:=TSubClass1.Create(Self);
end;
procedure TMainClass.SetSomeValueFromMainClass(Value: Integer);
begin
ValueFromAnySubClass := Value;
end;
procedure TMainClass.SetSubClass1(const Value: TSubClass1);
begin
FSubClass1 := Value;
FSubClass1.Owner:=Self;
end;
you must put the proper filename in uses part of implementation.

Resources