Thursday, May 24, 2007

Delphi Tips and Tricks

One of the most horrible features of Delphi is the fact that every class in the same unit have access to one another privates. This is similar to a friend class in C++ which should be used sparingly. As usual Delphi makes things easy which I guess is a good thing but like I said this is a horrible feature of the language. It ranks up there with "with". But like any horrible feature there are times when you can use it to your advantage. If a class is in a different unit but you need to get to the protected section just declare local class that derives from the class in question and cast your class to the local derivative and BAM! For example, take a look at CustDlg.pas:


type
THack = class(TWinControl);
THack2 = class(TControl);

procedure TCommandDockObject.EndDrag(Target: TObject; X, Y: Integer);
var
R: TRect;
begin
if Target is TToolBar then
begin
R := Control.BoundsRect;
R.Right := R.Left + TToolBar(Target).ButtonWidth;
THack2(Control).UpdateBoundsRect(R);
Control.Visible := True;
if not TToolBar(Target).AutoSize then
begin
TToolBar(Target).AutoSize := True;
TToolBar(Target).AutoSize := False;
end
else THack(Control.Parent).AdjustSize;
end;
inherited EndDrag(Target, X, Y);
end;


This feature of the language is being used to get access to TWinControl.AdjustSize and TControl.UpdateBoundsRect. Sick, isn't it?

5 comments:

gabr said...

Not if privates are strict privates.

Anonymous said...

This feature of the language is being used to get access to TWinControl.AdjustSize and TControl.UpdateBoundsRect. Sick, isn't it?

Ooo, I don't know - if this technique allowed accessing private members it would be. But given the way the VCL is structured, with base classes having most of their useful methods and properties protected (with each descendent class then usually making published much the same group of inherited properties), it's effectively a 'standard' way of treating many VCL components polymorphically - as your example demonstrates of course, nomenclature of 'THack' and 'THack2' notwithstanding. Now, if the VCL were to be re-engineered to use interfaces much more, matters might be different of course...

CR.

Anonymous said...

(By 'if this technique allowed accessing private members it would be', I meant to refer to how your example plainly isn't one of what you originally claimed as 'horrible', namely 'that every class in the same unit have access to one another's privates'. For of course, if TWinControl.AdjustSize and TControl.UpdateBoundsRect were marked as private, they couldn't be accessed in the way you demonstrate. Right, I'll stop trying to be a smart alec now...

CR.)

Chris Bensen said...

True, my example is of a protected not private member. I should have been more careful. However, I still stand by my statement of access to a classes privates in the same unit is "horrible". This exploit of that concept is interesting, useful and valid, which is exactly why I brought it up :)

Anonymous said...

We need a "protected internal" (like C#, IL) scope clause.

Many many times there are classes that need to access other classes in a "friendly" way, outside the unit.

Its a good luck we have the casting hack trick, otherwise we'd be forced to make public lots of things that aren't really intended to be visible by classes end-users.
The problem is with VCL.Net you cannot do that trick across assemblies, this is a real pity.

In Win32 you can still access really private fields (not protected), via casting the instance using a fake class with the same VMT and dummy fields to get to the offset of the field you want to access.

Regarding "with", it is also the same level of dangerousity to add one unit to the "uses" clause. As soon you add a new unit you're risky adding new global items (procs, funcs, types, variables, etc) to the scope, in the same way "with" does.
If units were classes or pure namespaces (like C#), then "uses MyUnit" will be like "with MyUnit".

Post a Comment