Tuesday, September 12, 2006

DN4DP#2: Protecting your privates

This post continues the series of The Delphi Language Chapter teasers from Jon Shemitz’ .NET 2.0 for Delphi Programmers book. Last time we learned about the new kinds of class members that have become available; class fields, class static methods, class properties and class constructors. This time we will look at the new class member visibility specifiers that are available, abstract classes and final methods.

Note that I do not get any royalties from the book and I highly recommend that you get your own copy – for instance at Amazon.

"Protecting your privates

Native Delphi already had four class member visibility levels; private, protected, public and published[1]. One quirk with these is that private and protected members are fully visible to all the code in the unit they are declared in[2], not just the class they are part of (almost like an implicit version of the C++ friend concept). To match .NET's concept of truly private and protected, two new access levels named strict private and strict protected, were introduced. The AppendixDfn\PrivateParts project demonstrates this

TFoo = class
strict private
FCantTouchMe: integer;
FAnyOneAndDelphiRTTI: integer;
FClassAndUnit: integer;
strict protected
FClassAndDescendants: integer;
FClassDescendantsAndUnit: integer;
FAnyOne: integer;
constructor Create(Report: boolean = True);
property AnyOneAndDelphiRTTI: integer read FAnyOneAndDelphiRTTI
write FAnyOneAndDelphiRTTI;

Delphi classes can now be explicitly sealed and abstract. The syntax here has the mildly surprising order class sealed and class abstract. The main reason for this is to avoid reserving more language keywords than absolutely necessary – sealed and abstract are directives that only have special meaning after the class reserved word. This means that existing code that already use these identifiers will not break. A sealed class cannot be inherited from and an abstract class cannot be instantiated (even if it does not contain any abstract methods).

Finally, a virtual method that you override can now be marked final, preventing derived classes from overriding that method.

  TAbstractClass = class abstract
procedure Bar; virtual;

TSealedClass = class sealed(TAbstractClass)
procedure Bar; override;

TFinalMethodClass = class(TAbstractClass)
procedure Bar; override; final;

[1] There is also the obsolete automated section from Delphi 2 – but that is not supported in .NET.

[2] One native Delphi trick is to declare a local descendant of a class in the current unit. Then you hard-cast an object instance into this local class. Now you have access to all the protected members of the object. This hack is so common that the .NET compiler has special logic to handle it too. It does work as long as the original class code is in the same assembly as the hacking code. This is because Delphi’s protected access maps to the .NET family or assembly access level. Similarly, Delphi’s private access maps to the .NET assembly access level, but the Delphi compiler itself enforces the cross-unit privateness."

[Note: This text differs slightly from the final printed version]


Arturo Martínez said...

Hi Hallvard.

I've been programming with Delphi for 10 years now, and I thought that I knew pretty much everything I needed to know to make my applications. After reading your last 10 posts, I must say that I was totally wrong.

Thank you very much for taking the time to share with us your knowledge in the Delphi RTTI internals. It has been very helpful.


Дмитрий Тимохов said...

Hi, Hallvard.

As i know ther abstract key word dont work for classes :(

I mean that abstract class could be instantinated.

See my report http://qc.codegear.com/wc/qcmain.aspx?d=24662

Hallvard Vassbotn said...

Hi Dimitry,

[Sorry for the *very* late answer ;)]

Yes, you are right. The compiler accepts the "class abstract" syntax, but does not actually warn aginst creating such classes - until you have one or more virtual abstract methods.

This is still the case in Delphi 2007 - hopefullyt this will be fixed in the next version. It has been reported to CodeGear - thanks to your QC report.

Copyright © 2004-2007 by Hallvard Vassbotn