Let's say I have two classes in Unit1: TParent = class(TCustomControl) and TDescendant = class(TParent) and they both have a lot of methods, fields and properties.
In Unit2 I need to modify TParent. Let's say I only need to modify a single method, Method1, in a very simple way that doesn't really affect anything outside itself (say, print a text in red color instead of black). I also need TDescendant to be affected by this modification (so every time it calls Method1 the modified Method1 is executed).
Is there a way to do that in Delphi 7, without modifying Unit1 and without copying the entire TDescendant class into Unit2?
On top of that I really need the class names to remain the same (I'm simply trying to modify a method in a 3rd party control without creating a whole new control to do it).
I made an interceptor class of TParent in Unit2, but I don't know how (if at all possible) to "tell" TDescendant to become a descendant of the interceptor class instead of the original.
As far as I know this is not possible without some serious low level hacks (pointer/memory based). Therefor I would not suggest is at all... (also I don't know the actual answer to that solution).
When you only want to add some extra methods to the TParent class, you can use helper classes (Delphi XE+ - maybe even earlier versions).
Ex.
TParentHelper = class helper for TParent
public
procedure MyMethod(param: string);
end;
Now you can dos stuff like:
uses
MyParentHelperU;
Procedure test(D: TDescendant);
begin
D.MyMethod('test');
end;
Notes:
you need to include the unit in which the helper class in defined, in order to use this functionality.
You can only add methods to the class (and descendants), for which the helper class has been assigned. You cannot add properties or fields.
Protected properties can be accessed by the helperclass, but are not always shown by the IDE
Related
In my application i need to add some extra functionality to 3rd party controls.
An example would be TcxLabel and TcxDBLabel (from DevExpress).
Both inherit from the the same base class.
For those controls i would like to add some fields and methods.
The way i do it today:
TMycxLabel = class(TcxLabel)
private
FMyField1: string;
FMyField2: Integer;
public
procedure DoSomething;
end;
TMycxDBLabel = class(TcxDBLabel)
private
FMyField1: string;
FMyField2: Integer;
public
procedure DoSomething;
end;
So basically i have to write everything twice.
One way to achieve it would be modifying the base class those 2 controls inherit from.
But that is not an option - the DevExpress classses / packages should not be modified.
Is there a way to add achieve this?
Is there a way to add achieve this?
No there is not. Any viable solution is going to be essentially equivalent to your current approach. Since you want the state associated with the instance, you realistically need it to be part of the instance. Which leads you to the solution that you have.
You could contemplate hijacking a field like Tag to store that state, but that would be wrong because Tag is reserved for use by the consumer of the type. Other plausible solutions might involve maintaining a separate list which mapped instance to this additional state. You could make that work but in my view it would be so unwieldy as to be worse than the alternative.
The most effective approach would be to add the fields to a common base class but you have ruled that out since it involves modifying third party code.
There is a way, although it will serve only if you only need what is available in the base class.
For instance, if TcxDBLabel inherits from TcxLabel, you can add a method (loosely speaking) to both as long as you only use the TcxLabel part (fields, methods and properties) of the TcxDBLabel.
I'm referring to typecasting.
I should warn that the use of this practice is not consensual. Many developers regard typecasting as a bad practice, and I myself tend to use it only as last resort. However, I'll leave to you the philosophical discussion of weather you should or shouldn't use this.
So if you create a subclass like this:
THcxLabel = Class(TcxLabel)
private
FMyField1: string;
FMyField2: Integer;
public
procedure DoSomething;
End;
You can then add this to any TcxLabel based class instance, like so:
THcxLabel(cxLabel1).DoSomething;
THcxLabel(cxDBLabel1).DoSomething;
Like I said, no specifics from TcxDBLabel will be available to you inside the DoSomething method, but you will be able to use the base class resources.
Could you inherit once to a virtual class (which is "yours"), define the new fields there, then inherit the two new classes from there?
Edit based on comments:
well you could try smth like this.
In BOTH your inherited classes, just add one public data member, which is itself a class, where you define your own stuff... looks like:
TMycxLabel = class(TcxLabel)
public
MyStuff: TMyStuff;
end;
TMyStuff = class()
private
FMyField1: string;
FMyField2: Integer;
public
procedure DoSomething;
end;
then call it like
cxLabel1.MyStuff.DoSomething();
I realize this doesn't completely answer your question, and doesn't work for several use cases (you can't access all attributes of TMycxLabel from MyStuff this way), but maybe you can avoid SOME double coding.
I have a class that descents from TObject, if I do not call the parent method, is it bad? I ask because when looking at TObject.Create/Destroy, they do nothing.
That's why we override/expose them ourselves.
There's no code example, I just want to make sure.
The most common behaviour is to call inherited. So if I explicitly do not want the inherited behaviour, I do not call inherited. Otherwise, I do. If the inherited behaviour is a no-op like TObject.Create/Destroy, I still call inherited.
Note also that the situtation for constructors and destructors is perhaps a little different than for other methods. It's exceptionally rare that you would need to skip a call to the inherited method. I cannot think of an example. Invariably constructors are creating and destroying other objects, so how could you contemplate skipping that?
I know that some authors write code that omits inherited when deriving directly from TObject, because they know that the implementation in TObject does nothing. I do not like that and to me it is a mistake to do that. The implementation details of TObject should not be leaking into derived classes.
I'm pretty sure that TObject.Create/Destroy will always be no-ops. If Embarcadero changed that then so much code would break. But what about one of your classes. Suppose you have a class derived from TObject. And then you have another class derived from that:
TMyClass1 = class
....
end;
TMyClass2 = class(TMyClass)
....
end;
You have no constructor for TMyClass1 and a constructor for TMyClass2 that looks like this:
constructor TMyClass2.Create;
begin
// no need to call inherited, it's a no-op
FMyObj := TBlahBlah.Create;
end;
Then one day you modify the TMyClass1 constructor to do something. Now TClass2 is broken. So, I would never ever omit a call to inherited because that call does nothing.
The situation for normal instance methods is a little different. It is more plausible that you want to ignore the base class implementation and provide a brand new implementation. But take the decision based on what the derived class wants to do rather than whether or not the super class has an empty implementation for the method.
You only need to call inherited if you want the code of the ancestors to execute. Sometimtes you need that code, sometimes you do not. You can also use conditionals and call inherited only if some condition is fulfilled, and you can choose when to do it (beginning of method, end of method, ...).
Hence, it all depends on the situation.
Here's an example.
Even though TObject's Create/Destroy methods don't do anything in the current version, they might in some future version (unlikely, but still might). So is it a MUST for you right now? No, because it won't accomplish anything, but only because the current parent doesn't add any functionality.
If you want your descendant objects to always add functionality to their parents, it would probably be a good idea to consistently call inherited.
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.
I would like to have some suggestions for the following problem:
Let's say you want to write adapters for the VCL controls. All Adapters should have the same base class, but differ in wrapping special controls (for example getting a value from a TEdit is different from getting a value from TSpinEdit).
So the first idea is to create a class hierarchy like
TAdapter = class
end;
TEditAdapter = class (TAdapter)
end;
TSpinEditAdapter = class (TAdapter)
end;
Now I want to introduce a field to hold a reference to the vcl control. In my special adapaters I want - of course - work with the concrete subclass. But the Base class should also contain a reference (for example if I want to use the adapter to make a control visible).
Possibility 1 (Downcast in Property Accessor):
TAdapter = class
protected
FCtrl : TControl;
end;
TEditAdapter = class (TAdapter)
public
property Control : TEdit read GetControl write Setcontrol;
end;
{...}
function TEditAdapter.GetControl : TEdit;
begin
Result := FCtrl as TEdit;
end;
So if I implement a specific method I work with the property Control, if I do something in my base class I use the protected field.
Possibility 2 (Use a generic base class):
TAdapter = class
end;
TAdapter <T : TControl> = class (TAdapter)
protected
FCtrl : T;
end;
TEditAdapter = class (TAdapter <TEdit>)
end;
Which solution would you prefer? Or is there a third solution, which is even better?
Kind regards,
Christian
You can't use generics to solve this problem, because you'll be in one of two situations:
The property or method you want to "Adapt" (the Text property for example) is defined in an ancestor class. In that case you don't need generics because you can use the one adapter for the ancestor and solve the problem for all descendants.
The property or method is introduced by the class you want to adapt. In that case you can't use generics because in order to access the property or method you'd need a generic type constraint of that given type. Example. Let's say you want an adapter for the Text property of TMyClass. Let's assume TMyClass is the one introducing the Text property. In order to access it, you'd need to declare the generic type as TGeneric<T:TMyClass> and that's not actually generic.
In my opinion the best bet is to write specific adapters for each class, as in your first option. You might be able to use RTTI tricks to make your first option easier to implement, but I'm not sure it'd be worth it.
The generics version could allow to avoid some duplicated code, at least in the TAdapter class. By using the T type, it will allow a lot of shared code.
On the other hand, due to the VCL hierarchy, most used properties and methods will already be in TControl. So I'm not sure there will be so many duplicated code in non-generic implementation.
I suspect the non-generic version will produce less code and RTTI, since the current generics implementation tends not to duplicate the source, but increase the exe size.
IMHO the generic-based design will add more abstraction to the implementation, but the non-generic would perhaps be more close to the VCL hierarchy it will adapt on.
So for your particular case (mapping the VCL), since your attempt is to map non generic classes, I'd rather investigate into the non-generic solution.
For another (non VCL-based) adapter architecture, I would probably have advised a pure generic implementation, from the bottom up.
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.