Delphi - can a property read from a property? - delphi

So I just joined this forum because I could not find an answer to my simple question.
I want to declare a read-only property, and it should read from a private members read-only property. It seems like that won't work. Can I work myself around that blockade?
Here is the code snippet:
property Mine: TMineType read mMine.MineType;
Edit: Maybe I should clarify. mMine is of class TMine, which has a property MineType. MineType is of type TMineType and is read-only.

A property getter can be one of two things:
A field, or
A function.
Your code attempts to implement the getter with a property, and that does not meet the requirement stated above.
The documentation contains all the details: http://docwiki.embarcadero.com/RADStudio/en/Properties

On a practical level, you could do something like this:
function GetMineType : TMineType;
begin
result := mMine.MineType;
end;
property Mine: TMineType read GetMineType;
If you declare the GetMineType function as inline it will not generate any code.

Based on your additional information that you provided in your comments (such information should be put into the original question itself right from the start) I believe I can provide an answer which would hopefully provide solution to the problem you are facing.
In the code bellow I'm showing of how you can make parent class property accessible through descendant class by using property forwarding or property publishing.
//Base class we use
TBaseClass = class(TObject)
//Private section. Methods, fields and properties declared here are only visible from within classes declared in same unit
//but not from classes or method declared in other units.
private
FPrivateField: Integer;
function GetPrivateField: Integer;
procedure SetPrivateField(AValue: Integer);
//This property is making use of getter and setter methods in order to access FPrivateField
property PrivateFieldProperty: Integer read GetPrivateField write SetPrivateField;
//Strict private section. Methods, fields and properties declared here are only visible from descendant classes and their
//methods but not from other classes and their methods even if they are declared in the same unit.
strict private
FStrictPrivateField: Integer;
function GetStrictPrivateField: Integer;
procedure SetStrictPrivateField(AValue: Integer);
//Public section. Methods, Field and properties declared here are visible from any other class or methods regardless of
//where they are declared
public
//This property is accessing its field directly
property DirectPrivateFieldProperty: Integer read FPrivateField write FPrivateField;
//This property is making use of getter and setter methods in order to access FStrictPrivateField
property StrictPrivateFieldProperty: Integer read GetStrictPrivateField write SetStrictPrivateField;
end;
//Sub class is actually a descendant class from our base class
TSubClass = class(TBaseClass)
public
//If your parent class already has declared certain property and you want to use that property in your descendant
//class you can use so called "property forwarding" approach where you only specify the parents property name that
//you want to make available to your descendant class. This approach only allows accessing fields that are declared
//in parents class.
//If you need to access field declared in descendant class then its property must directly access that field or use
//getter and/or setter methods declared in descendant class. Such approach is called property overloading.
property DirectPrivateFieldProperty;
property PrivateFieldProperty;
//If you want to limit the access level of a descendants class property (making it read only) you need to follow standard
//property declaration guideline where you declare property type and the field to which it is accessing either directly or
//by using getter or setter methods
property ReadOnlyPrivateFieldProperty1: Integer read FPrivateField;
property ReadOnlyPrivatefieldProperty2: Integer read GetPrivateField;
//You can also declare forwarded descendant class property with different visibility of the one declared in parent class.
//Such approach is usually referred as property publishing.
property StrictPrivateFieldProperty;
end;
Note the above approaches only work when accessing properties or fields of a parent class from a descendant class but not for accessing fields or properties of other classes.
The reason for this is that property may only directly access fields of its own class or any parent classes.
Also property can only use getter and setter methods that are declared either in its own class or in any of the parent classes.
I hope this answer of mine might give you better idea about working with properties.
Now at the moment I guess properties might look a bit confusing to you but believe me when you will learn to work with them you will realize of how powerful they can actually be. I must admit that I'm still finding out new ways to use them in my projects every now and then.

Related

Delphi Use calling objects properties in added class method

I want to add a method to an existing Delphi class. I think basic class framework OK but need to access some properties of the object that called my method in my method. I can't seem to get anything to work.
old class TStringGrid
new class OptStringGrid where myNewMethod is referenced
//example of new class method
procedure myNewMethod (const Name: string);
begin
//Here is my question location.
// I would like to access properties of the calling object in this case
// testgrid. Like... i:= testgrid.rowcount;
end
// Unit Calling statements
var
testGrid : OptStringGrid;
i: integer;
begin
i := testgrid.myNewMethod(strName);
end;
New to Delphi, forgive my terminology if wrong please. I know example code not compilable. I'm looking for techniques to access the properties as described.
To access members of the object whose method is executing, you can use the Self variable. It's automatically declared and assigned inside any method body. In fact, its use is usually implicit — any members of the object are automatically in scope inside the method body. You generally only need to qualify member access with Self when there is already some other variable in the method that has the same name as the member you wish to use.
The key thing about implementing methods is that you need to make sure they're actually methods. The code shown in the question does not define myNewMethod as a method. Rather, it's a standalone subroutine. Only methods can be called on objects, and therefore only methods can have access to the objects they're called on.
A method declaration appears inside a class declaration. Yours might look something like this:
type
TOptStringGrid = class(TStringGrid)
public
function myNewMethod(const Name: string): Integer;
end;
A method definition appears in the implementation section of your unit, along with all your other subroutine bodies, just like all the event-handler implementations the IDE creates for you when you double-click events in the Object Inspector. Those are just ordinary methods.
What distinguishes a method implementation from an implementation of some other kind of subroutine is that the method name includes the name of the class it belongs to:
function TOptStringGrid.myNewMethod(const Name: string): Integer;
begin
// ...
end;
Observe the TOptStringGrid. portion in the code above. That's how the compiler knows that the method body belongs to that class and not anything else named myNewMethod.
Within that method body, you can access all the published, public, and protected members of the ancestor class, TStringGrid, including the RowCount property.

Class Reference as Property

Google is useless for these sorts of searches, because you get hundreds of millions of results absolutely none of which relate to the specific question.
The question is simply this:
Is it possible to have a Class Reference Property in Delphi?
If so, how?
Here's what I've tried...
type
TMyObject = class
// ...
end;
TMyObjectClass = class of TMyObject
TMyObjectA = class(TMyObject)
// specifics here
end;
TMyObjectB =class(TMyObject)
// specifics here
end;
TMyComponent = class(TComponent)
private
FObjectType: TMyObjectClass;
published
property ObjectType: TMyObjectClass read FObjectType write FObjectType;
end;
The above code compiles fine, however the Object Inspector does not show the ObjectType property at all.
My objective here (if you haven't already guessed) is to make it so that I can select a class descendant from a specific base class, to make the same component behave in a different way.
I want to do it this way so that the component doesn't need to know about the sub-classes directly (it needs to be fully modular).
Let me just make this bit clear: I cannot use an Enum to choose between the sub-class types as the component cannot directly link to the sub-class types (It's simply not possible in this particular case)
Anyway... thanks in advance!
You can find all classes that descend from a particular base class: Delphi: At runtime find classes that descend from a given base class? and make this a special property with list of values using TPropertyEditor.
If you were going to do this then you would need to provide a property editor. The IDE does not come with property editors for class type properties. You would also need to handle .dfm persistence. You would write the class type out to the .dfm file as a string and when the .dfm file is read, you would need to fixup the reference. New style RTTI could do that.
However, I don't think any of this is actually viable for the following reason. Your design time code runs in a package inside the IDE and does not have access to the class types in the active project in the IDE. Those class types only exist when that project runs. So the ObjectType property in the code in your question cannot be assigned to anything meaningful in the design time package. Well, you could use it for classes defined in the VCL and any other packages installed in your IDE but I rather imagine you'd want to use it on classes defined in the active project.
I think all this means that you should instead use a simple string property and fixup the class type references only at runtime.

Delphi class references... aka metaclasses... when to use them

I've read the official documentation and I understand what class references are but I fail to see when and why they are best solution compared to alternatives.
The example cited in the documentation is TCollection which can be instantiated with any descendant of TCollectionItem. The justification for using a class reference is that it allows you to invoke class methods whose type is unknown at compile time (I assume this is the compile time of TCollection). I'm just not seeing how using TCollectionItemClass as an argument is superior to using TCollectionItem. TCollection would still be able to hold any descendant of TCollectionItem and still be able to invoke any method declared in TCollectionItem. Wouldn't it?
Compare this with a generic collection. TObjectList appears to offer much the same functionality as TCollection with the added benefit of type safety. You are freed from the requirement to inherit from TCollectionItem in order to store your object type and you can make a collection as type specific as you want. And if you need to access item's members from within the collection you can use generic constraints. Other than the fact that class references are available to programmers prior to Delphi 2009 is there any other compelling reason to use them over generic containers?
The other example given in the documentation is passing a class reference to a function that acts as an object factory. In this case a factory for creating objects of type TControl. Its not really apparent but I'm assuming the TControl factory is invoking the constructor of the descendant type passed to it rather than the constructor of TControl. If this is the case then I'm starting to see at least some reason for using class references.
So I guess what I'm really trying to understand is when and where class references are most appropriate and what do they buy a developer?
MetaClasses and "class procedures"
MetaClasses are all about "class procedures". Starting with a basic class:
type
TAlgorithm = class
public
class procedure DoSomething;virtual;
end;
Because DoSomething is a class procedure we can call it without an instance of TAlgorithm (it behaves like any other global procedure). We can do this:
TAlgorithm.DoSomething; // this is perfectly valid and doesn't require an instance of TAlgorithm
Once we've got this setup we might create some alternative algorithms, all sharing some bits and pieces of the base algorithm. Like this:
type
TAlgorithm = class
protected
class procedure DoSomethingThatAllDescendentsNeedToDo;
public
class procedure DoSomething;virtual;
end;
TAlgorithmA = class(TAlgorithm)
public
class procedure DoSomething;override;
end;
TAlgorithmB = class(TAlgorithm)
public
class procedure DoSomething;override;
end;
We've now got one base class and two descendent classes. The following code is perfectly valid because we declared the methods as "class" methods:
TAlgorithm.DoSomething;
TAlgorithmA.DoSomething;
TAlgorithmB.DoSomething;
Let's introduce the class of type:
type
TAlgorithmClass = class of TAlgorithm;
procedure Test;
var X:TAlgorithmClass; // This holds a reference to the CLASS, not a instance of the CLASS!
begin
X := TAlgorithm; // Valid because TAlgorithmClass is supposed to be a "class of TAlgorithm"
X := TAlgorithmA; // Also valid because TAlgorithmA is an TAlgorithm!
X := TAlgorithmB;
end;
TAlgorithmClass is a data type that can be used like any other data type, it can be stored in a variable, passed as a parameter to a function. In other words we can do this:
procedure CrunchSomeData(Algo:TAlgorithmClass);
begin
Algo.DoSomething;
end;
CrunchSomeData(TAlgorithmA);
In this example the procedure CrunchSomeData can use any variation of the algorithm as long as it's an descendant of TAlgorithm.
Here's an example of how this behavior may be used in a real-world application: Imagine a payroll-type application, where some numbers need to be calculated according to an algorithm that's defined by Law. It's conceivable this algorithm will change in time, because the Law is some times changed. Our application needs to calculate salaries for both the current year (using the up-to-date calculator) and for other years, using older versions of the algorithm. Here's how things might look like:
// Algorithm definition
TTaxDeductionCalculator = class
public
class function ComputeTaxDeduction(Something, SomeOtherThing, ThisOtherThing):Currency;virtual;
end;
// Algorithm "factory"
function GetTaxDeductionCalculator(Year:Integer):TTaxDeductionCalculator;
begin
case Year of
2001: Result := TTaxDeductionCalculator_2001;
2006: Result := TTaxDeductionCalculator_2006;
else Result := TTaxDeductionCalculator_2010;
end;
end;
// And we'd use it from some other complex algorithm
procedure Compute;
begin
Taxes := (NetSalary - GetTaxDeductionCalculator(Year).ComputeTaxDeduction(...)) * 0.16;
end;
Virtual Constructors
A Delphi Constructor works just like a "class function"; If we have a metaclass, and the metaclass knows about an virtual constructor, we're able to create instances of descendant types. This is used by TCollection's IDE Editor to create new items when you hit the "Add" button. All TCollection needs to get this working is a MetaClass for a TCollectionItem.
Yes a Collection would still be able to hold any descendant of TCollectionItem and to invoke methods on it. BUT, it wouldn't be able to instantiate a new instance of any descendant of a TCollectionItem. Calling TCollectionItem.Create constructs an instance of TCollectionItem, whereas
private
FItemClass: TCollectionItemClass;
...
function AddItem: TCollectionItem;
begin
Result := FItemClass.Create;
end;
would construct an instance of whatever class of TCollectionItem descendant is held in FItemClass.
I haven't done much with generic containers, but I think that given a choice, I would opt for the generic container. But in either case I'd still have to use a metaclass if I wanted to have the list instantiate and do whatever else needs to be done when an item is added in the container and I do not know the exact class beforehand.
For example an observable TObjectList descendant (or generic container) could have something like:
function AddItem(aClass: TItemClass): TItem;
begin
Result := Add(aClass.Create);
FObservers.Notify(Result, cnNew);
...
end;
I guess in short the advantage/benefit of metaclasses is any method/class having only knowledge of
type
TMyThing = class(TObject)
end;
TMyThingClass = class of TMyThing;
is able to construct instances of any descendant of TMyThing whereever they may have been declared.
Generics are very useful, and I agree that TObjectList<T> is (usually) more useful than TCollection. But class references are more useful for different scenarios. They're really part of a different paradigm. For example, class references can be useful when you have a virtual method that needs to be overridden. Virtual method overrides have to have the same signature as the original, so the Generics paradigm doesn't apply here.
One place where class references are used heavily is in form streaming. View a DFM as text sometime, and you'll see that every object is referred to by name and class. (And the name is optional, actually.) When the form reader reads the first line of an object definition, it gets the name of the class. It looks it up in a lookup table and retrieves a class reference, and uses that class reference to call that class's override of TComponent.Create(AOwner: TComponent) so it can instantiate the right kind of object, and then it starts applying the properties described in the DFM. This is the sort of thing that class references buy you, and it can't be done with generics.
I also would use a metaclass whenever I need to be able to make a factory that can construct not only one hard-coded class, but any class that inherits from my base class.
Metaclasses are not the term I am familiar with in Delphi circles however. I believe we call them class references, which has a less "magic" sounding name, so it's great that you put both common monikers in your question.
A concrete example of a place I have seen this used well is in the JVCL JvDocking components where a "docking style" component provides metaclass information to the base docking component set, so that when a user drags their mouse and docks a client form to the docking host form, the "tab host" and "conjoin host" forms that show grabber bars (similar in appearance to the title bar of a regular undocked window) can be of a user-defined plug-in class, that provides a customized appearance and customized runtime functionality, on a plug-in basis.
In some of my applications I have a mechanism that connects classes to forms capable of editing instances of one or more of that classes. I have a central list where those pairs are stored: a class refernce and a form class reference. Thus when I have an instance of a class I can lookup the corresponding form class, create a form from it and let it edit the instance.
Of course this could also be implemented by having a class method returning the appropriate form class, but that would require the form class to be known by the class. My approach makes a more modular system. The form must be aware of the class, but not the other way round. This can be a key point when you cannot change the classes.

Attributes don't inherit?

I played with attributes and assumed that they are inherited but it doesn't seem so:
type
[MyAttribute]
TClass1 = class
end;
TClass2 = class(TClass1)
end;
TClass2 doesn't have the Attribute "MyAttribute" although it inherits from Class1. Is there any possibility to make an attribute inheritable? Or do I have to go up the class hierarchy and search for attributes?
An attribute is a decoration for a class or other symbol, such as a method. An attribute decorates the declaration, but is not a feature of the class. As a result, an attribute is not inherited.
Yes, you could go up the class hierarchy to look for the attribute, but there is a better solution. Use an empty interface ( IMyInterface = Interface) as a "marker" interface. All descendants of an interface-implementing class will also be implementers of that interface. All you need to ensure is that your base class implements IInterface, which will already be the case if your base class descends from TInterfacedObject or TComponent.
Once you've done this, you can use the Supports function to test if the class, or one of its ancestors, implements the interface.
I never used attributes in Delphi - so this answer is kind of speculation. But I know about annotations in Java which is basically the same thing.
But it makes sense if they are not inherited: a subclass might require other attributes, or contradict attributes from a super class. Also, if attributes are not inherited you have a chance to follow the hierarchy if "your" attribute usecase needs that. If they were inherited you would have trouble to detect whether an attribute is actually on a particular class as opposed to any of its superclasses.
If you need inheritance and don't want to look at super classes it might make more sense to use a class function, or class property, or even a tag interface (declares no methods) instead. Those are inherited.

Interfaces and properties

Is it possible to declare a property in an interface without declaring the get- and set-methods for it? Something like:
IValue = interface
property value: double;
end;
I want to state that the implementor should have a property called value, returning a double, but I really don't care if it returns a private field or the result from a function.
If it is possible, is it possible to declare it read/write or read-only?
No. Interfaces are implemented as function tables (basically a simple virtual method table) and the compiler needs to know there's a function to map the property onto. You can declare a property on an interface, but it has to have functions as getter/setter values, not fields. You can make it read-only or write-only, though.
When working with properties in an interface, think of the property as a shortcut to the reader/writer. Only one is required to satisfy the shortcut...otherwise it doesn't point to anything.

Resources