Delphi for .NET compiler magic: splitting a large class into two files. And much more: extending without inheriting, extending sealed classes, ...
One of the new language features in Delphi for .NET are Class helpers. In simple words, a class helper is a construct that extends a class with all fields from the helper class. It allows you to extend existing class without actually modifying it or inheriting from it. The main reason Borland needed to extend the language with class helpers was to make VCL portable to .NET.
Here's an example (no implementations, only class definition / method interface):
TTest = class
procedure SomeProc;
end;
TTestHelper = class helper for TTest
function SomeFunc : string;
end;
With the above class definitions, we can use the next code:
var
t:TTest;
begin
t:=TTest.Create;
t.SomeFunc ;
...
even though SomeFunc was *not* defined in class TTest!
Class Helpers: Implications (and Limitations)
Let's consider some implications of Class Helpers. Class Helpers allow Delphi developers to:- Extend existing CLR ("native" .NET) classes without modifying their implementation.
- Extend existing CLR classes without inheriting from them.
- Extend sealed classes. Note: A sealed class is one that cannot be extended through inheritance.
- Split a (large) class definition across 2 (or more) source files.
- A class helper cannot access (strict) private fields of the class it extends. Note: this is not a true limitation.
- A class helper cannot implement an interface for the class it extends.
- You cannot define a class to be a helper for a class that already is a class helper.
TTest = class; //ok
THelper = class helper for TTest; //ok
TDoubleHelper = class helper for THelperClass. // NOT ok
While extending existing classes can come really helpful, the ability to span a class definition into 2 source files ads several nifty features to Delphi developers:
- Class helpers are great when several developers work on the same class. Without Class Helpers team would have to use source code management features such as check-in/check-out and merge changes for multiple programmers to work on a single class; with class helpers, each team member can work on a separate part of the class, and the system will handle merging the separate code files back into a single class at compile time.
- If your class is auto created using some kind of code generator, using class helpers would save you from losing custom parts of the class, when the class needs to be regenerated. Simply add any additional methods to a class helper.
- Suppose you need to maintain two versions of your product, a component maybe, where one version has more features than the other one (i.e. "Professional" and "Standard" version of a component). Class helpers allow you to add all the "Professional" features in a separate file. When you build the "Standard" version, you simply exclude (from the project) the unit where the class helper was used!
Class Helper Example
To understand how class helpers operate, I'll present you with one example. The "ClassHelpers " is a console mode application. The project contains two units. Unit "Test.pas" defines the TTest class with one class procedure "TestProcedure". Unit "TestHelper.pas" defines a TTestHelper class as a class helper for TTest. TTestHelper ads a procedure "HelperProcedure" to the TTest class.~~~~~~~~~~~~~~~~~~~~~~~~~
program ClassHelpers;
{$APPTYPE CONSOLE}
uses
Test in 'Test.pas',
TestHelper in 'TestHelper.pas';
begin
TTest.TestProcedure; //defined in TTest
TTest.HelperProcedure; // defined in TTestHelper
end.
unit Test;
interface
type
TTest = class
class procedure TestProcedure;
end;
unit TestHelper;
interface
uses Test;
type
TTestHelper = class helper for TTest
class procedure HelperProcedure;
end;
~~~~~~~~~~~~~~~~~~~~~~~~~
Download full source.
