I. Introduction

Le but de cet article n'est pas d'expliquer ce qu'est .NET, il y a une présentation ici par Microsoft, mais de présenter ce qu'il va être possible de faire avec Delphi for .NET et ce que j'ai pu tester.  Avec Delphi 7, le compilateur en ligne de commande (dccil) ainsi que les premières unités de ce qui sera la VCL for .NET sont  livrés. Il est donc dès à présent possible de commencer à tâter le terrain de ce que sera l'avenir de Delphi sur cette plateforme. Le produit final dont le nom de code est Galileo sera disponible courant 2003.

II. Le Hello World

Pour commencer un programme simple :

 
Sélectionnez
program HelloWorld;

{$APPTYPE CONSOLE}

begin
  Writeln('Hello World in Delphi for .NET');
end.

Image non disponible

On compile le programme avec le compilateur en ligne de commande dccil qui va produire un exécutable contenant du code MSIL (MicroSoft Intermediate Language) conforme comme on peut le vérifier avec le programme peverify livré avec le SDK .NET.

Mais à partir de cette preview, il est aussi possible d'accéder au framework .NET. Voici une application simple l'utilisant :

 
Sélectionnez
unit Main_frm;

interface

uses
  System.Windows.Forms,
  System.Drawing;

type
  TfrmMain = class(Form)
  private
    btnOk: Button;
    procedure InitializeComponents;
  public
    constructor Create;
    procedure btnOkClick(Sender: TObject; E: EventArgs);
  end;

implementation

{ TfrmMain }

procedure TfrmMain.btnOkClick(Sender: TObject; E: EventArgs);
begin
  MessageBox.Show('Coucou', 'Delphi for .NET preview');
end;

constructor TfrmMain.Create;
begin
  inherited Create;
  InitializeComponents;
end;

procedure TfrmMain.InitializeComponents;
begin
  btnOk := Button.Create;
  btnOk.Parent := Self;
  btnOk.Location := Point.Create(10,10);
  btnOk.Size := Size.Create(200,25);
  btnOk.Text := 'Coucou';
  btnOk.add_Click(btnOkClick);
end;

end.
 
Sélectionnez
program GUI;

uses
  System.Windows.Forms,
  Main_frm in 'Main_frm.pas';

begin
  Application.Run(TfrmMain.Create);
end.

Image non disponible

Dans cet exemple, on crée à partir des classes du framework .NET une application fenêtrée.  On déclare une classe dérivant de System.Windows.Forms.Form avec un bouton (System.Windows.Forms.Button). Dans le constructeur de la fiche on va initialiser notre composant bouton (taille, position, texte, etc.) et on va lui ajouter un gestionnaire d'événement avec la méthode add_Click correspondant à l'événement Click. On remarque ici la gestion par ressources de Delphi est quand même beaucoup plus pratique qu'une initialisation par code… ;-)

On obtient donc une application fenêtrée plutôt rapidement en utilisant seulement le framework .NET. Il est donc plus simple avec .NET de créer de telles applications qu'avec les API WIn32. On retrouve une programmation proche de la VCL au niveau du système.

Si vous compilez avec l'option -V, les informations de debuggage sont incluses et il est possible d'utiliser le debugger de la CLR (Common Language Runtime) DbgCLR fournit avec le Framework SDK (C:\Program Files\Microsoft.NET\FrameworkSDK\GuiDebug\DbgCLR.exe).

Image non disponible

Menu Debug | Program To Debug, indiquez le programme que vous venez de compiler avec l'option -V. Puis ouvrez le fichier source Pascal que vous voulez debugger. Posez un point d'arrêt (F9) à la ligne qui vous intéresse et lancez le programme sous contrôle du debugger (F5). Le programme s'arrêtera sur le point d'arrêt.

Image non disponible

Vous pouvez alors faire du pas à pas (Step Over, F10) ou du pas à pas approfondi (Step In, F11).

III. Les unités et le Framework .NET

La compilation des unités Delphi produit maintenant un fichier .dcuil. À chaque fois que l'on fait un uses dans un programme on a besoin du fichier .dcuil associé. Cela signifie que dans notre exemple, lorsque l'on fait uses System.Windows.Forms; il faut le fichier System.Windows.Forms.dcuil. Le compilateur dccil est livré avec ces fichiers dcuil pour l'ensemble du framework .NET. Avec ces dcuil il existe des fichiers .dcua qui représentent une description des assemblies contenues dans le namespace du dcuil. Le compilateur examine le fichier dcua pour savoir quel dcuil il doit recompiler quand une assembly a changé.

Il est maintenant possible de créer des namespaces pour les unités :

 
Sélectionnez
unit MyCompany.MyApplication.MyForm;

interface

implementation

end.

Cette unité sera par exemple compilée en MyCompany.MyApplication.MyForm.dcuil et on l'utilisera de la façon suivante :

 
Sélectionnez
program MyApplication;

uses MyCompany.MyApplication.MyForm;

begin

end.

Il est possible et parfois nécessaire de préfixer par le namespace entier certains identificateurs :

 
Sélectionnez
 type
  TfrmMain = class(Form)
  private
    lblName: System.Windows.Forms.Label;
    procedure InitializeComponents;
  public
    constructor Create;
  end;

var
  AType: System.Type;

Label et type étant des mots réservés du langage Pascal, il est important de préciser le namespace pour indiquer au compilateur que ce n'est pas l'utilisation du mot réservé mais d'un type du framework .NET

Puisque les dcuil du framework .NET sont livrés avec le compilateur vous avez à disposition des milliers de classes prêtes à l'emploi et que je vous invite à explorer. Vous avez la référence de ces classes ici: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/cpref_start.asp

Les exécutables produits par dccil s'exécutant maintenant dans l'environnement managed de la CLR, la libération des objets se fait automatiquement par le Garbage Collector. En général, Il n'est donc  plus nécessaire d'appeler Free ou de définir un destructor. Pour des questions de compatibilité du code existant, Free est toujours défini et peut être appelé, mais la libération mémoire de l'objet n'est pas assuré après cet appel. Néanmoins, il est nécessaire de libérer dans les destructors les ressources qui ne seraient pas managed, c'est à dire non prise en compte par la CLR.

La classe TObject est maintenant aliasée sur le System.Object du framework .NET ce qui permet d'avoir une interaction plus homogène avec le reste du framework. De même, les types de base du pascal vont correspondre aux types de la CLR.

IV. Les Class Helpers

La création d'un nouveau compilateur pour l'environnement .NET a permis d'introduire des nouveautés dans le langage. L'une des plus puissantes qui m'a été donné de voir est celle des class helpers. Cette technique a été ajouté pour répondre à la problématique de compatibilité entre la VCL Win32 et le framework .NET. La classe TObject, ancêtre de toutes classes en Delphi est maintenant définie comme étant la classe System.Object de .NET. Or la classe .NET ne possède pas les méthodes du TObject que l'on avait avec la version Win32. Pour pallier ce manque, Borland a crée une construction que l'on appelle Class Helper permettant d'élargir la portée d'une classe avec les méthodes définies dans le class helper.

Voyons cela sur un exemple concret :

 
Sélectionnez
program ClassHelperExample;

{$APPTYPE CONSOLE}

type
  TMyClass = class(TObject)
  public
    procedure AMethod;
  end;

  TMyClassHelper = class helper for TMyCLass
    procedure AClassHelperMethod;
  end;

{ TMyClass }

procedure TMyClass.AMethod;
begin
  Writeln('TMyClass.AMethod');
end;

{ TMyClassHelper }

procedure TMyClassHelper.AClassHelperMethod;
begin
  Writeln('TMyClassHelper.AClassHelperMethod');
end;

var
  MyClass: TMyClass;

begin
  MyClass := TMyClass.Create;
  MyClass.AMethod;
  MyClass.AClassHelperMethod;
end.

On déclare tout d'abord, une classe TMyClass avec une méthode AMethod. Puis on déclare un class helper TMyClassHelper pour la classe TMyClass. Dans ce class helper on déclare une méthode AClassHelperMethod. On implémente cette dernière comme faisant partie de la classe TMyClassHelper, mais en réalité elle va pouvoir être appelée à partir d'une instance de TMyClass comme on le voit dans le programme principal.

Voyons alors, avec ce mécanisme, ce que donne l'implémentation de TObject pour Delphi for .NET :

 
Sélectionnez
type
  TObject = System.Object;

  TObjectHelper = class helper for TObject
    procedure Free;
    class function ClassType: TClass;
    class function ClassName: string;
    class function ClassNameIs(const Name: string): Boolean;
    class function ClassParent: TClass;
    class function ClassInfo: TObject;
    class function InheritsFrom(AClass: TClass): Boolean;
    class function MethodAddress(const Name: string): TObject;
    function FieldAddress(const Name: string): TObject;
    procedure Dispatch(var Message: TObject);
    procedure DefaultHandler(var Message: TObject);
  end;

TObject bénéficie ainsi de toutes les méthodes définies dans System.Object du framework .NET plus les méthodes définies dans le class helper. Bien sûr, toutes les classes descendantes de TObject en bénéficie aussi.

Il est aussi possible d'hériter d'un autre helper class.

 
Sélectionnez
TAnotherClassHelper = class helper(TMyClassHelper) for TMyCLass
  procedure AnotherClassHelperMethod;
end;

Nous avons vu le principe de base des class helpers, à vous maintenant d'explorer les comportements en essayant différentes combinaisons sur des exemples comme ci-dessus.

V. Les nouveautés du langage

Comme l'environnement d'exécution qu'est .NET demande un code dit safe, certaines constructions deviennent non valides et obsolètes pour Delphi for .NET comme les pointeurs et tout ce qui se rapporte directement à la mémoire, les allocations de mémoire (GetMem, FreeMem, ReallocMem)  les fichiers typés, les transtypages dans des types incompatibles, etc. Delphi 7 intègre des avertissements quant à la nature safe du code que vous compilez pour Win32. Ces avertissements peuvent être activés pour tester la qualité de votre code en vue d'être porté vers .NET.

En plus des class helpers que l'on a vu précédemment, Delphi for .NET intégrera d'autres nouveautés, qui sont pour l'instant encore en recherche. En vrac on peut citer :

  • les méthodes et classes finales (non héritables) ;
  • les types imbriqués (définir une classe dans une autre) ;
  • méthodes dans les records (mais non virtuelles). Les records deviennent ainsi des objets automatiques (sans allocation dynamique, mais sur la pile) ;
  • variables de classe (static) ;
  • propriétés de classe (static) ;
  • gestionnaire d'événements multiples (Include et Exclude seront surchargés pour ajouter ou retirer un gestionnaire) ;
  • surcharge de propriétés (property overload) ;
  • etc.

VI. Conclusion

Le compilateur Delphi for .NET livré avec Delphi 7 est une version que l'on pourrait encore qualifier d'alpha. Il reste beaucoup de choses à implémenter, mais on peut déjà commencer à jouer avec et à utiliser les classes du framework .NET ce qui est déjà énorme ! Cela donne un bon avant goût de ce que sera Galileo courant 2003 et on espère une compatibilité proche de la VCL pour migrer facilement les applications vers cette nouvelle plateforme.