IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Le langage Delphi pour .NET

Le langage Delphi pour .NET


précédentsommairesuivant

1. Public concerné

Image non disponible


Testé sous le framework .NET 1.1 et Delphi 2005.
Version 1.0

Concernant l'EDI de Delphi 2005 vous pouvez consulter l'article de SJRD : Prise en main de Delphi 2005.

Cet article contient des liens nommés Local. Ils pointent sur des URLs locales de la documentation de Delphi 2005 et de Microsoft. Leur utilisation nécessitera l'installation du SDK .NET v1.1 en français sur le poste.

Ils sont uniquement accessibles Offline, les liens 'ms-help' exécutent en local l'aide en ligne du SDK.

1-1. Les sources

Les fichiers sources des différents exemples :
FTP.
HTTP.

L'article au format .PDF.

2. Aperçu de la plate-forme .NET

Extrait du SDK :
"Le .NET Framework est une nouvelle plate-forme informatique qui simplifie le développement d'applications dans l'environnement fortement distribué d'Internet. Le .NET Framework est conçu pour remplir les objectifs suivants :

  • Fournir un environnement cohérent de programmation orientée objet que le code objet soit stocké et exécuté localement, exécuté localement mais distribué sur Internet ou exécuté à distance.
  • Fournir un environnement d'exécution de code qui minimise le déploiement de logiciel et de conflits de versioning.
  • Fournir un environnement d'exécution de code qui garantit l'exécution sécurisée de code y compris le code créé par un tiers d'un niveau de confiance moyen ou un tiers inconnu.
  • Fournir un environnement d'exécution de code qui élimine les problèmes de performance des environnements interprétés ou écrits en scripts.
  • Fournir au développeur un environnement cohérent entre une grande variété de types d'applications comme les applications Windows et les applications Web.
  • Générer toutes les communications à partir des normes d'industries pour s'assurer que le code basé sur le .NET Framework peut s'intégrer à n'importe quel autre code.

Le .NET Framework contient deux composants principaux : le Common Language Runtime et la bibliothèque de classes du .NET Framework. Le Common Language Runtime est la base du .Net Framework. Le runtime peut être considéré comme un agent qui manage le code au moment de l'exécution, fournit des services essentiels comme la gestion de la mémoire, la gestion des threads, et l'accès distant. Il applique également une stricte sécurité des types et d'autres formes d'exactitude du code qui garantissent un code sécurisé et robuste. En fait, le concept de gestion de code est un principe fondamental du runtime. Le code qui cible le runtime porte le nom de code managé, tandis que le code qui ne cible pas le runtime porte le nom de code non managé. La bibliothèque de classes, l'autre composant principal du .NET Framework, est une collection complète orientée objet, de types réutilisables que vous pouvez utiliser pour développer des applications allant des traditionnelles applications à ligne de commande ou à interface graphique utilisateur (GUI, Graphical User Interface) jusqu'à des applications qui exploitent les dernières innovations fournies par ASP.NET, comme les services Web XML et Web Forms.
..."
Lire la suite. Local.

2-1. Quelques détails sur le framework .NET

Le Runtime
Il se situe dans le répertoire C:\WINDOWS\Microsoft.NET\Framework.

Le SDK
Il se situe dans C:\Program Files\Microsoft.NET\SDK\v1.1\, n'hésitez pas à le parcourir il contient évidemment de nombreux outils, sources et informations diverses.

Le GLobal Assembly Cache (GAC)
Le GAC remplace selon Microsoft la registry pour la centralisation des informations d'assemblage de .NET.
Il se situe dans le répertoire %Windir%\assembly sa visualisation dans l'exploreur se fait d'une manière différente des autres répertoires, ici l'exploreur charge la DLL Shfusion.dll qui est une extension du shell Windows (local) permettant de visualiser son contenu. Bien évidement il est possible de le visualiser en 'mode fichier' dans une console Windows.

Attention la manipulation du GAC doit se faire au travers d'outils appropriés, voir le cache de l'assembly global. Local.

A noter que certaines clés de registre référencent des entrées du GAC :
HKEY_CLASSES_ROOT\Installer\Assemblies et HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Installer\Assemblies.

Vous trouverez dans cette clé HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework le répertoire d'installation du SDK et du Runtime

Recopiez, dans le répertoire contenant le SDK .NET, le script "...\Microsoft.NET\SDK\v1.1\Bin\sdkvars.bat" dans le répertoire C:\Windows. Vous pourrez ainsi déclarer les variables d'environnement selon les besoins.

3. Introduction

De nouvelles fonctionnalités ont été ajoutées au langage Delphi pour .NET afin de supporter des concepts et des fonctionnalités de programmation propre à la plate-forme .NET, et ce tout en respectant la CLS (Common Language Specification).

Cette adaptation pouvant laisser croire qu'il s'agit d'un nouveau langage alors qu'en fait ce qui est nouveau c'est la plate-forme .NET, c'est une évidence qu'il est bon de rappeler, votre premier objectif étant d'abandonner certaines pratiques et habitudes liées à la programmation sous Win32, qui à mon sens sont un frein à la compréhension de .NET. N'essayez donc pas de faire du WIN32 sous .NET, mais ceci est plus facile à dire qu'a faire ;-).

Selon moi, un des points important est qu'étant donné que sous .NET tout est objet vous devez 'penser' objet, plus encore que sous Win32.
Bien évidemment les problématiques telles que la gestion des threads ne changent pas ( .NET s'appuyant sur WIN32 ), seule leur implémentation diffère.

J'aborderais ici ces nouveautés du langage ainsi que les différences de comportement les plus sensibles. Ces quelques informations permettront aux débutants sous Delphi pour .NET, d'appréhender au mieux les bases de .NET et du langage Delphi pour .NET. Pour simplifier, j'ai principalement utilisé des applications console dans les exemples proposés.
Cette introduction n'aborde pas les problématiques de migration de code Win32 vers .NET, consultez à ce sujet l'ouvrage d'Olivier Dahan ou les articles appropriés sur le site de Borland US.

De nombreuses informations issues du SDK .NET émailleront cet article, autant puiser à la source.

4. Les pointeurs n'existent plus sous DotNet ?

Sous Dot Net la manipulation d'adresse n'est plus possible dans du code managé étant donné que l'organisation de la mémoire est désormais du domaine exclusif du Garbage Collector (ramasse-miettes) et plus du programmeur.
Cette prise en charge de la mémoire par le Garbage Collector implique une réorganisation périodique de la mémoire, on ne peut donc plus avoir de référence fixe sur des zones mémoire.

La manipulation de pointeur reste possible mais dans ce cas votre code est considéré comme unsafe, non sécurisé. Ceci pouvant avoir comme conséquence de limiter ou d'interdire l'intégration de ce code dans d'autres applications .NET.

Voir aussi
Garbage Collector : Le GC est un sous-ensemble de la CLR. Local.

Le Common Language Runtime (CLR) gère la mémoire, l'exécution des threads, l'exécution du code, la vérification de la sécurité du code, la compilation et d'autres services du système. Ces fonctionnalités font partie intégrante du code managé qui s'exécute sous le Common Language Runtime. Local.

L'article sur les pointeurs sous .NET par Nono40.

Certains liens peuvent ne pas s'afficher directement, dans ce cas valider à nouveau l'URL dans votre browser.

5. Type primitif

Les types primitifs sous .NET sont Boolean, Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, Char, Double et Single.
Ils correspondent directement aux types qui existent dans le Framework .NET

Type Delphi Mappage de types .NET
Boolean Boolean
Shortint SByte
Byte Byte
Smallint Int16
Word UInt16
Longint Int32
Integer Int32
Longword UInt32
Int64 Int64
Cardinal UInt32
Char Char
Single Single
Double Double

6. Value Types et Reference Types

Les allocations de blocs de données, par exemple, se font désormais soit dans la pile soit sur le tas (Managed Heap). Si on ne manipule plus d'adresse comment dans ce cas passer en paramètre un objet sans avoir à recopier l'intégralité de son contenu sur la pile ?

Sous .Net les données sont de deux types :

  1. Type valeur
    Les types valeur contiennent directement leurs données; les instances de types valeur sont allouées sur la pile.
  2. Type référence
    Les types référence stockent une référence à l'adresse mémoire de la valeur et sont alloués sur le tas managé.

Je tiens à remercier Emerica qui m'a proposé ce graphique, m'évitant ainsi des explications des plus hasardeuses sur le sujet et confirmant par là même qu'un dessin vaut mieux qu'un long discours.

Image non disponible
Hiérarchie des types sous Dot NET.

Les variables de type valeur stockent des données alors que les variables de type référence stockent les références aux données. Les types référence sont également considérés comme des objets comme on peut le voir sur le schéma précédent.

On voit donc que la notion d'indirection persiste sous .Net mais n'a plus à rien à voir avec les pointeurs présent sous Win32, Win16 ou MS-DOS. La possibilité d'interpréter des données pointées, c'est à dire la possibilité de transtyper des données, est également affectée. On retrouve donc la notion de typage fort, chère au Pascalien.

Il faut savoir que sous .NET tout est objet. La classe System.Object prend en charge toutes les classes de la hiérarchie des classes du .NET Framework et fournit des services de bas niveau aux classes dérivées. Il s'agit de la classe de base fondamentale parmi toutes les classes du .NET Framework. Elle constitue la racine de la hiérarchie des types.

Voir Aussi
Vue d'ensemble du système de type commun. Local.
Qu'est-ce que la spécification CLS (Common Language Specification) ? Local.

6-1. Principales fonctionnalités des types valeur

  • Une variable d'un type valeur contient toujours une valeur de ce type.
  • L'assignation d'un type valeur à une variable entraîne la création d'une copie de la valeur assignée alors que l'assignation d'un type référence à une variable crée une copie de la référence mais pas de l'objet référencé.
  • Tous les types valeur sont dérivés implicitement de la classe .NET System.Object.
  • Les variables d'un type valeur sont généralement des types primitifs et assignées sur la pile.
  • Contrairement aux types référence, il n'est pas possible de dériver un nouveau type à partir d'un type valeur existant. En revanche, comme les types référence, les types Record peuvent implémenter des interfaces.
  • Un type valeur ne peut pas contenir de valeur null/NIL, à la différence d'un type référence.
  • Chaque type valeur possède implicitement un constructeur par défaut qui initialise la valeur par défaut du type associé.
  • Puisqu'un type de valeur contient une copie de ses données, la modification d'un type de valeur modifiera seulement cet exemple particulier.
  • Cependant, la modification d'un type de référence modifiera toutes les instances qui ont une référence à ce type.

Si vous souhaitez créer un nouveau type par valeur, vous devez le dériver de System.ValueType, c'est à dire à partir d'un Record sous Delphi .NET. Les classes étant utilisées pour créer de nouveau type par référence.

6-2. Un peu de pratique

Projet : ..\TypeValue-Ref\ValeurReference

Nous aborderons plus avant le détail des nouvelles fonctionnalités associées au type de donnée Record.
Chargez le projet et regardons ici la modification d'un type valeur :

 
Sélectionnez
Type
 monRecord=Record
  A :Integer ;
  B :Integer ;
  Constructor Create(p1,p2:integer);
 end ;

 Constructor monRecord.Create(p1,p2:integer);
  begin
   A:=p1;
   B:=p2;
  end;

var
   RInstance1, RInstance2 : monRecord;

Begin
 RInstance1:= monRecord.Create(5, 3) ; // Création d'un objet.
                                       // L'assignation directe des champs par RInstance1.A 9 est possible.
 RInstance2:= RInstance1; // Recopie de l'objet RInstance1 dans RInstance2
 RInstance1.A:= 7; //  Le champ RInstance2.A reste égale à 2.

 Writeln('RInstance1.A=',RInstance1.A);
 Writeln('RInstance2.A=',Rinstance2.A);
 Readln;
End;

On constate la recopie membre par membre de RInstance1 vers RInstance2. Dans cet exemple nous avons bien 2 instances différentes.
Passons à la modification d'un type référence :

 
Sélectionnez
Type
 maClasse=Class
  A :Integer ;
  B :Integer ;
  Constructor Create(p1,p2:integer);
 end ;

Constructor maClasse.Create(p1,p2:integer);
  begin
   inherited Create;
   A:=p1;
   B:=p2;
  end;

var
   CInstance1, CInstance2 : maClasse;

Begin

 CInstance1:= maClasse.Create(2, 9) ; // Création d'un objet
 CInstance2:= CInstance1; // copie la référence d'Instance1 dans CInstance2
 CInstance1.A:= 1; //  Le champ Instance2.A dans ce cas est aussi égal à 1.

 Console.Writeline; // Appel d'une classe du Framework .NET 
					// Alias FCL ( Framework Classe Library)
 Writeln('CInstance1.A=',CInstance1.A);
 Writeln('CInstance2.A=',Cinstance2.A);
 Console.readline;
End;

Dans cet exemple on constate que CInstance1 référence bien CInstance2, nous avons bien 2 instances identiques.
Sous Delphi .NET on voit donc qu'une classe est un type référence alors qu'un record est un type valeur.

Allons plus loin en mixant les 2 types et essayons de récupérer une référence d'une variable de type valeur.
Ajoutons d'abord la déclaration d'une variable de type TObject qui permet de référencer n'importe quel objet :

 
Sélectionnez
var
   OTemp: TObject;

Ensuite

 
Sélectionnez
 OTemp:=RInstance1;     // Récupére une référence sur RInstance1
 RInstance1.A:=20;      // Modifie la variable référencée
 Writeln('OTemp.A transtypé en monRecord, référence RInstance1.A=',monRecord(OTemp).A);
 Writeln('RInstance1.A référencé par OTemp=',RInstance1.A);
 Writeln;
 Readln;

Nous nous apercevons que le résultat ne correspond pas à nos attentes. Ici l'instruction OTemp:=RInstance1; n'affecte pas l'adresse( la référence ) de la variable RInstance1 mais effectue une copie compléte de l'objet RInstance1 dans OTemp avec allocation de la mémoire nécessaire !

Continuons notre démarche, essayons de modifier une variable de type valeur en lui assignant un type valeur :

 
Sélectionnez
 Writeln('Assignation d''un type valeur avec une variable de type référence'+#13#10);
 RInstance1:=MonRecord(OTemp);
 Writeln('RInstance1.A est réassigné avec OTemp=',RInstance1.A);
 Writeln('OTemp.A=',MonRecord(OTemp).A);
 Writeln;
 Readln;

Ici aussi il y a 2 variables distinctes.
Un type valeur ne pouvant tout simplement pas se transformer en un type référence !
Par contre il est possible de convertir un type valeur en type référence, et inversement, en effectuant une conversion que l'on nomme Boxing et unBoxing.

Dans le source vous trouverez le dernier cas de figure qui concerne l'assignation de type référence qui lui ne pose pas de problème particulier de compréhension.

Par contre vous trouverez cette suite d'instructions qui est plus du 'bricolage' qu'autre chose :

 
Sélectionnez
try
  OTemp:=Rinstance1;
  CInstance1:=maClasse(OTemp); 
  Writeln('Test de bricolage ',CInstance1.A);
  Console.readline;
 except
  On E: NullReferenceException do
   Console.WriteLine('Erreur d''exécution la variable CInstance1 contient une référence nulle !');
  end;
 end;

Et ceci pour mettre en évidence qu'une variable instanciée de type référence peut se retrouver avec une assignation NULL, assignation qui provoquera l'exception NullReferenceException.

Voir aussi : Système de type commun. Local.

7. Conversion

Certaines traductions d'ouvrage sur .NET associent le terme boxing à une notion de compartiment.

7-1. Boxing

Projet : ..\Boxing-unBoxing\Boxing

Le boxing est une conversion explicite ( attention implicite en C# ) d'un type valeur en type objet ou bien vers n'importe quel type interface implémenté par ce type valeur. Lorsqu'une conversion boxing est appliquée à une variable de type valeur, une instance d'un objet est allouée et la valeur de cette variable est copiée dans le nouvel objet.

Examinons ce procédé dans l'exemple suivant :

 
Sélectionnez
var VarTypeValeur : integer;     // Type valeur
    VarTypeReference : TObject;  // Type objet ( référence )

Begin
  VarTypeValeur:= 123;
  VarTypeReference:=TObject(VarTypeValeur);  // Boxing explicite
  VarTypeValeur:= 456;                       // Change le contenu de i

  Writeln('Valeur de VarTypeValeur de type valeur = ', VarTypeValeur);
  Writeln('Valeur de VarTypeReference de type object (référence ) = ', VarTypeReference);

L'instruction VarTypeReference:=TObject(VarTypeValeur) réalise explicitement une conversion boxing sur la variable VarTypeValeur. Il ne s'agit pas ici d'un simple transtypage mais d'une opération à part entière masquée par le compilateur et prise en charge par le CLR.
Le boxing permet donc à chaque chose d'être vue comme un objet (un objet sous .NET étant un type référence).

A partir du moment où tout est objet on peut donc appeler les méthodes de System.Object sur les variables VarTypeValeur et VarTypeReference et particulièrement la méthode GetType qui renvoie de manière littérale le type de l'objet interrogé.

Ajoutons ces quelques lignes au début du programme précédent :

 
Sélectionnez
  VarTypeReference:=TObject.Create; // L'appel de GetType nécessite l'appel du constructeur

   // Gettype renvoie une valeur de type System.Type
  Writeln('Nom complet du type de VarTypeValeur= ', VarTypeValeur.GetType);
  Writeln('Nom complet du type de VarTypeReference= ', VarTypeReference.GetType);

L'affichage nous renvoie :

 
Sélectionnez
Nom complet du type de VarTypeValeur= System.Int32
Nom complet du type de VarTypeReference= System.Object

Ajoutons de nouveau les lignes d'affichage du type mais cette fois-ci après les modifications et l'affichage du contenu des variables.

 
Sélectionnez
Nom complet du type de VarTypeValeur= System.Int32
Nom complet du type de VarTypeReference= System.Int32

La variable VarTypeReference est désormais de type System.Int32, le boxing modifie donc le contenu et le type de l'objet cible.
Je vous laisse vérifier ce que donne l'instruction suivante :

 
Sélectionnez
 VarTypeReference:=TObject(321.1);

Note :

 
Sélectionnez
var w : Word;    // Type valeur
    i : integer; // Type valeur
    o : TObject; // Type objet ( référence )

Begin
  i:= 123;
  o:=i;
  ...

Le code ci-dessus provoquera une erreur de compilation sur la seconde ligne à cause du typage fort de Delphi. Dans ce cas présent, le programmeur peut faire deux choses :
- soit expliciter le transtypage de la variable I vers un TObject :

 
Sélectionnez
  o := TObject(I);

- soit ajoutez la directive du compilateur AUTOBOX, directive qui est à utiliser avec précaution.

L'opération suivante n'est plus autorisée par le compilateur : Word(i) := w;

Voir aussi
Conversion Boxing. Local.

7-2. UnBoxing

Projet : ..\Boxing-unBoxing\unBoxing

Il s'agit de l'opération inverse au boxing.
L'unboxing est une conversion explicite du type objet en un type valeur ou bien d'un type interface en un type valeur qui implémente l'interface.

Une conversion unboxing comprend les étapes suivantes :

  • Vérification de l'instance de l'objet pour s'assurer qu'il s'agit bien d'une valeur convertie du type valeur spécifié.
  • Copie de la valeur de l'instance dans la variable de type valeur.

Pour qu'une conversion unboxing vers un type valeur donné soit exécutée correctement, la valeur de l'argument source doit être une référence à un objet existant qui a été créé en effectuant une conversion d'une valeur du même type valeur.
Si l'argument source a la valeur nulle/Nil ou est une référence à un objet non pris en charge, une exception InvalidCastException est levée.

Examinons ce procédé dans l'exemple suivant :

 
Sélectionnez
var j,i : integer;
    box : TObject;

begin
  i:=312;            // Un type valeur
  box:=TObject(i);   // Boxing explicite
  j:=integer(box);   // Unboxing explicite

Ajoutons une variable de type Byte et le code suivant :

 
Sélectionnez
var
    h   : byte;
    j,i : integer;
    box : TObject;

begin
  ...
  H:=byte(box);
  Writeln('Nom complet du type de K= ', H.GetType);
  Writeln('Valeur de h, de type valeur, = ', h);

L'assignation de H à partir de la variable box provoque l'erreur InvalidCastException à l'exécution.
Pourtant ce transtypage est sensé mais il faut savoir que l'opération de unboxing doit se faire vers un type identique à celui utilisé lors de l'opération de boxing.

L'opération de transtypage doit donc se faire sur 2 niveaux :

  • le premier pour récupérer la valeur du type compartimenté.
  • le second vers le type final tout en respectant les conventions de conversion de Delphi pour .NET.
    Par exemple si le type contenu dans la variable box est de type double cette opération ne peut se faire qu'en utilisant l'instruction Trunc
 
Sélectionnez
var h   : Byte;
    box : TObject;
    K   : Double;

begin
  ...
  H:=byte(integer(box)); 
  ...


  K:=3.12;
  box:=TObject(K);   // Boxing explicite
  h:=byte(Trunc(Double(box)));     // Transtypage des types réels en entiers nécessite Trunc

Voir aussi
Conversion unBoxing. Local.

7-3. L'allocation mémoire

Projet : ..\AllocationArrayList\AllocationArrayList

A la différence de Win32, et en dehors de la création d'objet par son constructeur, il n'est plus nécessaire d'allouer explicitement de la mémoire pour manipuler un type enregistrement ou un type de base (nous verrons plus tard les constructeurs de type).

L'exemple suivant, en utilisant le boxing, nous le confirme :

 
Sélectionnez
//AllocationArrayList
uses
  System.Collections;

Type
  Data=Record
   X,Y : Integer;
  end;

var D   : Data;
    Tab : Arraylist;
    i   : Integer;

begin
  Tab:=Arraylist.Create;

  For i:=0 to 10 do
  begin
   D.X:=i;
   D.Y:=D.X*5;
   Tab.Add(D); // Ajout d'un record dans la liste,
               // ne nécessite pas d'allocation mémoire pour les multiples instances de D.
               // Box implicite
  end;

  For i:=0 to 10 do
   Writeln('X= '+Data(Tab[i]).X.ToString+' Y= '+Data(Tab[i]).Y.ToString);

  Readln;
end.

L'allocation est réalisée lors de l'appel de la méthode Tab.Add, par une opération de boxing qui comme nous l'avons vu :
Lorsqu'une conversion boxing est appliquée à une variable de type valeur, une instance d'un objet est allouée et la valeur de cette variable est copiée dans le nouvel objet.

8. Passage de paramètres

Projet : ..\Passage_Parametre\Parametre

Les type valeur et type référence se comportent différemment dans un appel de méthode selon qu'il sont passés soit en paramètre par valeur soit en paramètre par référence.

L'utilisation de var ou out permet de spécifier le passage de paramètre par référence, le mot-clé out permettant de passer en paramètre une valeur qui n'a pas été initialisée.
L'utilisation de const ou l'absence de mot-clé permet de spécifier le passage de paramètre par valeur.

Le tableau suivant récapitule les cas :

  1. Passage de types valeur par valeur.
    Les modifications intervenant au sein de la méthode n'affectent en rien la valeur d'origine de la variable.

  2. Passage de types valeur par référence.
    La valeur du paramètre est modifiée après l'appel de la méthode.

  3. Permutation de types valeur (deux entiers).
    Vous devez passer les paramètres à la méthode Swap par référence

  4. Passage de types référence par valeur.
    La tentative de réassignation du paramètre à un emplacement de mémoire différent s'effectue uniquement au sein de la méthode et n'affecte pas la variable d'origine

  5. Passage de types référence par référence.
    Toutes les modifications qui interviennent dans la méthode affecteront les variables d'origine dans le programme appelant.

  6. Permutation de types référence (deux chaînes).
    La permutation de chaînes est un bon exemple de passage de paramètres de type référence par référence. Les deux chaînes sont permutées au sein de la méthode, ainsi que dans l'appelant.

On voit donc que l'initialisation d'une variable globale, si toutefois cette notion a encore un sens sous .NET, se fera ainsi :

 
Sélectionnez
Procedure Init_Variable( Out uneRéference: unType);

8-1. Paramètre sans type

Projet : ..\Passage_Parametre\ParametreSansType

La déclaration d'un paramètre sans type dans un entête de méthode reste possible sous Delphi .NET . Dans ce cas la variable est vue comme une référence de type TObject à l'intérieur de la méthode. Un transtypage sera donc nécessaire pour la manipuler. De plus il est possible de lui assigner une nouvelle référence mais la classe de cette nouvelle référence doit être une classe identique ou dérivée mais pas une classe ancêtre. Ceci étant du à mon avis au typage fort de .NET.

 
Sélectionnez
procedure PrcAvecParametreSansType(var X);
begin
 Writeln('Type de la variable X : '+TypeOf(x).ToString);
end;

9. Déclarations de types imbriqués dans les classes

Vous pouvez consulter l'article suivant concernant les déclarations de type imbriqués.

9-1. Visibilité des types imbriqués.

Les membres (d'une classe ou d'un enregistrement) des types imbriqués peuvent avoir des visibilités différentes de la classe qui les héberge.
Déclarons une classe imbriquée comme ceci :

 
Sélectionnez
type 
  TForm1 = class(TForm) //Déclaration de classe qui héberge une déclaration de classe imbriqué.
  private 
    { Déclarations privées} 
  public 
    Type 
      TSousClasse = Class (Tobject) // Classe imbriquée
      Private 
        MembrePrivé : Integer; 
      Public 
        MembrePublic : Integer; 
      End; 
    { Déclarations publiques} 
  public 
    SousClasse : TSousClasse; 
  end;

Bien que TSousClasse soit public, de même que son instance (SousClasse), il ne sera pas possible d'accéder en dehors de l'unité à TForm1.SousClasse.MembrePrivé.

 
Sélectionnez
procedure TForm2.Button1Click(Sender: TObject); 
Var i:Integer; 
begin 
  i:=Form1.SousClasse.MembrePublic; // Compile 
  i:=Form1.SousClasse.MembrePrivé; // Ne compile pas 
end;

Je remercie Nono40 pour cet exemple.

10. Alias de type

Projet : ..\Type\Type_type

Delphi permettait déjà de créer des alias de type tels que

 
Sélectionnez
type TMaString = string;

autorisant la déclaration de variable suivante :

 
Sélectionnez
 var 
  Chaine1: TMaString;
  Chaine2: string;

Ici Chaine1 et Chaine2 sont du même type.
Si on souhaite distinguer le type exact de ces 2 variables cela n'est pas possible car TMaString est un nom de type différent de string mais si on veut créer un autre type, vu comme tel par le compilateur, il faut utiliser la syntaxe suivante :

 
Sélectionnez
type TMaString = type string;

Dans ce cas le passage en paramètre par référence d'une variable de ce type provoquera une erreur de compilation :

 
Sélectionnez
type
  TMaString1 = string;
  TMaString2 = type string;

 var
  Chaine: string;
  Chaine1: TMaString1;
  Chaine2: TMaString2;

  procedure Affiche(S:TMaString2);
  begin
  end;

  procedure Affiche_Var(Var S:TMaString2);
  begin
  end;

begin
 Chaine:='Type Chaine classique';
 Chaine1:=Chaine;
  // l'affectation ne pose pas de problème
 Chaine2:=Chaine;

  // Le passage en paramètre à une procédure const ou par valeur ne pose pas de problème
 Affiche(Chaine1);


  // Le passage en paramètre à une procédure var, provoque l'erreur (E2033) :
  // Les types des paramètres var originaux et formels doivent être identiques.
  Affiche_Var(Chaine1);

Le code précédent provoque l'erreur suivante :

 
Sélectionnez
E2033 : Les types des paramètres var originaux et formels doivent être identiques.

11. Identificateurs étendus

Texte issu de la documentation de DELPHI 2005 :

Lors de la programmation avec Delphi pour .NET, il se peut que vous rencontriez des identificateurs CLR (c'est-à-dire des types ou des méthodes de classe) de même nom qu'un mot-clé Delphi. Par exemple, une classe peut posséder une méthode appelée begin. La classe CLR Type de l'espace de nommage System constitue un autre exemple. Type est un mot-clé du langage Delphi qui ne peut être utilisé comme nom d'identificateur.

En revanche, vous pouvez sans problème qualifier l'identificateur à l'aide de sa spécification complète de l'espace de nommage. Pour utiliser la classe Type, par exemple, vous devez utiliser son nom complet :

 
Sélectionnez
var
 TMyType : System.Type; // L'utilisation de l'espace de nommage qualifié
                        // évite toute ambiguïté avec le mot-clé Delphi.

Comme alternative abrégée, Delphi pour .NET propose l'opérateur perluète (&) afin de résoudre les ambiguïtés entre identificateurs CLR et mots-clé du langage Delphi. Si vous rencontrez une méthode ou un type portant le même nom qu'un mot-clé Delphi alors que vous avez indiqué le signe & au début de l'identificateur, vous pouvez omettre la spécification de l'espace de nommage.

Par exemple, le code suivant utilise un "et commercial" pour distinguer la classe CLR Type du mot-clé Delphi type.

 
Sélectionnez
var
   TMyType : &Type; // Le mot commence par '&' ce qui le distingue du mot-clé.


Un autre exemple dans un contexte de conversion de code C# vers Delphi.

12. Itérateurs

Vous pouvez consulter l'article suivant concernant les itérateurs.

 
Sélectionnez
var I, odd, even : integer;
  arr  : Array[] of integer;

Begin
 odd:= 0;
 even:=0;
 arr:= New(array[] of integer,(0,1,2,5,7,8,11));

 For I in arr Do
 begin
  WriteLn('I= '+ intToStr(I));
  if (I mod 2 = 0)
   then
    begin
    inc(even);
    WriteLn('Trouvé '+ intToStr(even)+' '+intToStr(I));
    end
   else inc(odd);
 end;
 WriteLn('Trouvé '+ intToStr(odd)+' chiffre impair et '+intToStr(even)+' chiffre pair.') ;
 Readln;

Attention ce qui différencie une boucle classique d'un itérateur est qu'on manipule via la variable d'indice I le contenu de la collection.
La variable I ne contient pas le nombre d'itération.
Cette syntaxe peut prêter à confusion dans les premiers temps.

Rappel des différentes possibilités :

 
Sélectionnez
 // Tableau
const
  MyArr : Array[0..3] of char = ('1','2','3','4');
var
  C : Char;
  S : String;
begin
  for C in MyArr do
    S := S + C;
  ShowMessage(S);
end;


 // Ensemble
const
  MySet: set of char = ['1','2','3','4'];
var
  C : Char;
  S : String;
begin
  for C in MySet do
    S := S + C;
  ShowMessage(S);
end;


 // Chaîne de caractère (Strings)
const
  S : String = 'C''est une chaine intéressante';
var
  C : Char;
  cnt: Integer;
begin
  cnt := 0;
  for C in S do
    if C = 'e' then
      Inc (cnt);
  ShowMessage(Format('Votre chaîne contient %d lettres "e"',[cnt]));
end;

 // Collection prédéfinie TStringLists 
var
  S : String;
begin
  for S in Memo1.Lines do
    ListBox1.Items.Add(S);
end;

Projet : ..\class-iterateur\class_iterateur

L'itération sur une classe demande un peu plus de code :

 
Sélectionnez
type
 TPeopleEnumerator = class; // Forward déclaration

   // Objet manipulé
  TPerson = class
    Name: String;
    DateOfBirth: TDateTime;
    Gender : Char;
    constructor Create (AName: String; ADateOfBirth: TDateTime; AGender: Char);
  end;

   // Liste d'objets, conteneur pour le type TPerson
  TPeopleList = class
  strict private
    List : TList;
    function GetCount: Integer;
    function GetPerson(idx: Integer): TPerson;
    procedure SetPerson(idx: Integer; const Value: TPerson);
  public
    Constructor Create;
    function Add (Person: TPerson): Integer;
    procedure Delete (idx: Integer);
    property Person [idx: Integer] : TPerson read GetPerson write SetPerson; default;
    property Count: Integer read GetCount;
          //Méthode nécessaire pour la prise en charge de l'itération
    Function GetEnumerator: TPeopleEnumerator;
  end;

   // Afin que la collection soit énumérable, elle doit implémenter une suite de méthodes
   // comme indiqué ci dessous.

   // Enumérateur pour la classe conteneur.
  TPeopleEnumerator = class
  strict private
    CurrentPos: Integer;
    PeopleList : TPeopleList;
  public
    constructor Create (AList : TPeopleList);
    function GetCurrent: TPerson;
     //Membre de classse nécessaire pour la prise en charge de l'itération
    function MoveNext :Boolean;    // Méthode publique
    property Current : TPerson read GetCurrent; // Propriété en Read Only
  end;

Une fois les méthodes implémentées, l'itération de la classe devient possible :

 
Sélectionnez
 //Exemple d'énumération de la classe TPeopleList
 // Le membre PeopleList appartient à la Forme principale
Procedure TForm1.Button1Click(Sender: TObject);
var Person : TPerson; // Doit être une variable locale
begin
  // Ajout d'élément
 PeopleList.Add(TPerson.Create('Daniel',StrToDate('15/03/1973'),'M'));
 PeopleList.Add(TPerson.Create('Monique',StrToDate('05/05/1973'),'F'));

  // Itération sur la classe
 for Person in PeopleList do
  ListBox1.Items.Add(Person.Name);
end;

La variable utilisée pour l'itération doit être de même type que celle déclarée dans la classe cible et cette variable doit être locale à la méthode ou à la procédure.

13. Directives de compilation

Vous pouvez consulter l'article suivant concernant les nouvelles directives.

13-1. Autobox

 
Sélectionnez
{$AUTOBOX ON}

La directive AUTOBOX génère un typage faible. L'employer peut faciliter l'écriture mais peut également amener plus d'erreurs de programmation.
Son utilisation force le boxing c'est à dire qu'elle évite au programmeur les opérations de transtypage nécessaires sans sa présence.

13-2. Experimental

Il s'agit d'une directive de conseil. Vous pouvez utiliser cette directive pour désigner des unités qui sont dans un état de développement instable. Le compilateur émet un avertissement quand il construit une application qui utilise l'unité.

 
Sélectionnez
unit Borland.Vcl.NetControl platform experimental;

14. Compilation conditionnelle

Nom Définition
VER170 Version du compilateur Delphi 2005. La documentation est erronée.
CLR Indique que le code sera compilé pour la plate-forme .NET.
WIN32 WIN32 Indique que le système d'exploitation est Win32 API.
Utilisez WIN32 pour distinguer les diverses plates-formes Windows, comme Windows 32 et 64 bits.
En général, ne limitez pas le code à WIN32 sauf si vous êtes certain qu'il ne fonctionnera pas en WIN64.
Utilisez à la place MSWINDOWS.

15. Commutateurs de la ligne de commande de l'IDE

Un nouveau commutateur, debugger=[borwin32|bordonet], vous permet de sélectionner le débogueur Win32 de Borland ou le débogueur .NET en ligne de commande du SDK, lorsque vous déboguez à partir de la ligne de commande.


Lors de l'utilisation du commutateur -LU sur la plate-forme .NET, vous pouvez faire une référence au package avec ou sans l'extension .dll.
Si vous omettez l'extension .dll, le compilateur recherchera le package sur le chemin de recherche de l'unité et sur le chemin de recherche du package. Néanmoins, si la spécification de package contient une lettre de lecteur ou le caractère séparateur de chemin, le compilateur suppose alors que le nom de package est le nom de fichier complet (y compris l'extension .dll). Dans le dernier cas, si vous spécifiez un chemin relatif ou complet, mais omettez l'extension .dll, le compilateur ne sera pas capable de localiser le package.

Voir aussi

16. Ce qui est déconseillé

Il n'est plus possible, dans du code managé, d'utiliser les déclarations ou procédures suivantes :
pointeurs, type procédure, exitproc, absolute, @, les types pchar et variants...

Pour la manipulation de délégués l'opérateur @ reste possible mais c'est une exception.

Les procédures de manipulation de mémoire : move, fillchar, getmem, freemem...
Et bien évidemment la directive asm, l'accès aux registres du micro-processeur n'est plus possible dans du code managé ( .NET ou JAVA ).

Et ceci pour la simple raison qu'ils sont considérés comme type ou code non sécurisé ( unsafe ).
Les types pointeur et pointeur de fonction ne sont pas conformes CLS. Certains types de données ne sont plus supportés et sont soit émulés soit remplacés par d'autres types natifs de .NET.

Le tableau suivant montre la correspondance entre les types réels fondamentaux et les types .NET Framework.
Mappage des types réels .NET fondamentaux

Type Mappage .NET
Real48 Deprecated
Single Single
Double Double
Extended Double
Comp Deprecated
Currency Ré-implémenté comme un type de valeur en utilisant le type Decimal du .NET Framework

Si votre composant doit être entièrement utilisable par des composants écrits dans d'autres langages, les types exportés de votre composant doivent exposer uniquement les fonctionnalités de langage qui font partie de la spécification CLS (Common Language Specification).

Voir aussi
Deprecated Language Features
Aide de delphi 2005 :Portage d'applications VCL vers Delphi 2005

17. Les délégués

Projet : ..\Delegues\Delegate1

Un délégué en Delphi .NET est similaire à un pointeur de fonction ou de procédure en WIN32. Avec un délégué, vous pouvez encapsuler une référence à une méthode à l'intérieur d'un objet delegate. Ce dernier peut ensuite être transmis au code, qui peut appeler la méthode référencée sans savoir, au moment de la compilation, quelle méthode sera appelée. Toutefois, à la différence des pointeurs de procédure ou de fonctions, les délégués sont orientés objet, indépendants des types et sécurisés.
Une de leur utilisation concerne les fonctions de rappel (Callback).

En Delphi .NET les types procédure sont donc implémentés comme des types délégués. Leur déclaration se fait par l'ajout de of object au nom de type. Exemple :

 
Sélectionnez
  TMonDelegue= Procedure(AClient:TClient) of Object;

Les déclarations suivantes sont toutes compilées comme des délégués :

 
Sélectionnez
Type
  TMonDelegue  = Procedure of Object;
  TMonDelegue2 = Procedure;
  TMonDelegueF = Function:Boolean;
var
   unDelegue : procedure(L :integer);

L'utilisation se fait ensuite de la manière suivante :

 
Sélectionnez
  TCompte=Class
   Clients:Array[1..5] of TClient;
   Constructor Create;
    // Déclaration
   procedure Callback(UnDelegue:TMonDelegue);
  end;
...
 // Implémentation
procedure TCompte.Callback(UnDelegue:TMonDelegue);
var i: integer;
begin
 For i:=1 to 5 do
    //Callback
   UnDelegue(Clients[i]);
end;
...
// Main  
Procedure AfficheClient(UnClient:TClient);
begin
 Writeln('Procédure de callback appelée pour le client nommé : ',UnClient.Nom);
end;

begin
 UnCompte:=TCompte.Create;
  // Appel
 UnCompte.Callback(AfficheClient);

Il est tout à fait possible de manipuler ce délégué par l'intermédiaire d'une variable typée :

 
Sélectionnez
Var TestDelegue:TMonDelegue;
..
  // Autre construction possible
 TestDelegue:=AfficheClient;
 UnCompte.Callback(TestDelegue);

Projet : ..\Delegues\Delegate2

Ensuite il est possible de visualiser la différence qui ne semble pas évidente entre une déclaration d'un type procédure sous Win32 et d'un délégué sous Delphi .NET:

 
Sélectionnez
  // Assignation obligatoire, sinon exception NullReferenceException lors de l'appel de GetType
 TestDelegue:=AfficheClient;
 Writeln('Ancêtre de TestDelegue = ',typeof(TestDelegue).BaseType);

L'instruction suivante provoque l'erreur de compilation suivante :

 
Sélectionnez
Pas assez de paramètres originaux (E2035)

La syntaxe correcte ici se fait par l'utilisation de l'opérateur @ qui semblait déconseillé. Il s'agit ici de la seule exception.
Il permet d'obtenir l'adresse du délégué. Si vous n'employez pas cette syntaxe, le compilateur pensera que vous essayez d'appeler le délégué, d'où l'erreur précédente.

 
Sélectionnez
 Writeln('Ancêtre de TestDelegue = ',typeof(@TestDelegue).BaseType);

 // Ici le compilateur reconnaît un accès à un membre de classe.
 Writeln('Type de TestDelegue = ',TestDelegue.GetType);

L'affichage de la classe ancêtre de TestDelegue nous renvoie System.MultiCastDelegate qui est elle-même dérivée de la classe System.Delegate.
En quoi notre procédure est-elle à diffusion multiple (multi-cast) ?

17-1. La diffusion multiple

Projet : ..\Delegues\Delegate3

Cet exemple nous permet d'aborder une nouveauté fort appréciable du langage Delphi pour .NET.
On peut désormais par un appel de procédure, notre délégué utilisé précédemment, enchaîner l'appel de plusieurs procédure et modifier ce chaînage au gré des besoins !
Nous avons vu jusqu'ici l'information multi, portée par le nom de classe; en associant cette mécanique aux événements (messages) on retrouve l'information de diffusion (cast), on peut donc désormais déclencher simplement des actions multiples à la suite de la réception d'un message.

Sous Delphi .NET, la déclaration d'une propriété est obligatoire pour implémenter ces fonctionnalités.
Voici la déclaration d'un délégué single-cast :

 
Sélectionnez
Property MonDelegue:TMonDelegue read FMonDelegue write FMonDelegue;

La déclaration d'un délégué multi-cast nécessite l'utilisation de nouveaux mot-clés, concernant les propriétés, qui sont add et remove.
Leur présence signale au compilateur qu'il autorise le chaînage :

 
Sélectionnez
Property MonDelegue:TMonDelegue add FMonDelegue remove FMonDelegue;

La différence entre les 2 déclarations est que pour la première il n'y a qu'un seul appel possible, il s'agit du dernier délégué ajouté à 'la liste de délégués' via la méthode include. Voyons dans le détail ce comportement.
On doit donc dans un premier temps modifier la classe TCompte et le prototype de sa procédure Callback :

 
Sélectionnez
  TCompte=Class
   Clients:Array[1..5] of TClient;
    // Pour le multicast doit être une propriété :
   FMonDelegue:TMonDelegue;
   Constructor Create;
   procedure Callback;
    // la syntaxe : Property MonDelegue:TMonDelegue read FMonDelegue write FMonDelegue;
    // ne permet pas le composition de délégué. Il n'y a qu'un seul appel qui est le dernier déclaré
   Property MonDelegue:TMonDelegue add FMonDelegue remove FMonDelegue;
  end;

Ensuite dans l'implémentation on doit appeler le délégué via la propriété :

 
Sélectionnez
procedure TCompte.Callback;
var i: integer;
begin
 if assigned(FMonDelegue) then
  For i:=1 to 5 do
    FMonDelegue(Clients[i]);
end;

Et pour finir l'ajout d'un délégué dans la liste se fait par l'appel de la procédure dédiée Include.

 
Sélectionnez
 Include(UnCompte.MonDelegue,@Traite);
 Include(UnCompte.MonDelegue,@AfficheClient);
 UnCompte.Callback;

La suppression d'un délégué dans la liste des délégués se fait par l'appel de la procédure dédiée Exclude.

 
Sélectionnez
 Exclude(UnCompte.MonDelegue,@Traite);
 Exclude(UnCompte.MonDelegue,@AfficheClient);
 Include(UnCompte.MonDelegue,@Fin);
 UnCompte.Callback;

Il reste possible d'assigner une méthode à délégué comme ceci :

 
Sélectionnez
 UnCompte.FMonDelegue:=Traite;
 UnCompte.Callback;

mais dans ce cas le compilateur crée un nouvel objet délégué et lui affecte la méthode indiquée. La suppression de l'ancienne référence contenue dans FMonDelegue étant prise en charge par le Garbage Collector.
La suppression complète peut se faire par l'affectation de NIL au délégué :

 
Sélectionnez
  // Identique à System.Delegate.RemoveAll(System.Delegate(@FMultiEvent), Nil);
 UnCompte.FMonDelegue:=Nil;   
 UnCompte.Callback;

17-2. Gestionnaire d'événements sous la VCL

Projet : ..\Delegues\VCL\Delegues

Vous trouverez dans cet exemple une utilisation d'un délégué au sein d'une Fenêtre VCL (TForm).

Il est possible de retrouver le contenu de la liste des délégués :

 
Sélectionnez
type
   //Tableau Dynamique de Délégués
  TDelegateDynArray = array of System.Delegate;
...

procedure TForm1.Button1Click(Sender: TObject);
var TabDelegue : TDelegateDynArray;
    unDelegue  : System.Delegate;
    
begin
  if Not assigned(FMultiEvent)
  then
   begin
    Log.Lines.Add('La liste des délégués est vide');
    Exit;
   end;
 TabDelegue:=System.Delegate(@FMultiEvent).GetInvocationList;
 For unDelegue in TabDelegue do
  begin
   // Obtient le type du Delegué, ici : TNotifyEvent
   //  log.Lines.Add('Array :'+Deleg.ToString);
  Log.Lines.Add('Array :'+' '+unDelegue.Target.ToString+'.'+unDelegue.Method.Name);
 end;
end;

Il est tout à fait possible de tester si 2 listes de délégués contiennent ou non des méthodes identiques.
Consultez la documentation du SDK à propos des tests d'égalité sur des instances de délégués : Delegate.Equals ...

Voir aussi :
Délégué EventHandler. Local
Classe EventArgs. Local

17-3. Gestionnaire d'événements sous Windows Form

La gestion des événements sous Windows Form est différente, on doit utiliser un prototype de délégué spécifique :

 
Sélectionnez
unit Borland.Vcl.NetControlWrapper platform experimental;
...
 TNetEventHandler = procedure (Sender: System.Object; AArgs: System.EventArgs);
...

Notez que la convention de nommage, préconisé par Microsoft, est d'ajouter le suffixe EventHandler. Libre à vous de la suivre.
L'entête de la méthode du délégué contient un premier paramètre de type System.Object et un deuxième de type System.EventArgs. Par convention, le premier paramètre est toujours l'instance qui a appelée le gestionnaire d'événement, le second contient des arguments décrivant l'événement.

La classe System.EventArgs ne contient pas de donnée d'événement; elle est utilisée par des événements qui ne passent pas d'informations d'état à un gestionnaire d'événements lorsqu'un événement est déclenché. Si le gestionnaire d'événements nécessite des informations d'état, l'application doit dériver une classe à partir de cette classe pour contenir les données.

 
Sélectionnez
type
 MyEventArgs=Class(EventArgs)

 UserPrintPageEventArgs = class(EventArgs)
  Public
   MySpecialValue :String;
 end;

 TSampleEventHandler = procedure(Sender: TObject; Args: MyEventArgs);

Lorsque le délégué est appelé, le gestionnaire a accès à Args.MySpecialValue.

Voir aussi :
Didacticiel sur les événements. Local
System.EventArgs. Local
Délégué EventHandler. Local

18. Syntaxe d'assistance de classe ( Class Helper )

Texte issu de la documentation de DELPHI 2005
Une assistance de classe est un type qui, lorsqu'il est associé à une autre classe, introduit des méthodes et propriétés supplémentaires qui peuvent être utilisées dans le contexte de la classe associée (ou ses descendants). Les assistances de classes permettent d'étendre une classe sans avoir recours à l'héritage.

Les assistances de classes permettent d'étendre une classe, mais elles ne doivent pas être considérées comme un outil de conception à utiliser pour développer un nouveau code. Elles ne doivent être utilisées que pour la liaison entre le langage et la RTL de la plate-forme.

On peut voir ci dessous l'utilisation parcimonieuse des class Helper dans les sources du runtime de Delphi 2005 .NET.

 
Sélectionnez
...\Borland\BDS\3.0\source\dotNet\rtl\
Borland.Delphi.System.pas :   TObjectHelper = class helper for TObject
Borland.Vcl.Classes.pas)  :   TPersistentHelper = class helper (TObjectHelper) for TPersistent
Borland.Vcl.Classes.pas   :   TComponentHelper = class helper (TPersistentHelper) for TComponent
Borland.Vcl.SysUtils.pas  :   ExceptionHelper = class helper (TObjectHelper) for Exception
Borland.Vcl.TypInfo.pas   :   TTypeInfoHelper = class helper for TTypeInfo
Borland.Vcl.TypInfo.pas   :   TPropInfoHelper = class helper for TPropInfo
Borland.Vcl.Variants.pas  :   VariantHelper = class helper for Variant
Borland.Vcl.Variants.pas  :   OleVariantHelper = class helper for OleVariant

Toutes les classes concernées sont des classes spécifiques à Delphi et mappées sur les classes correspondantes du Framework .NET. Par contre ces classes du Framework ne déclarent pas certains membres présents dans la classe Delphi.

Le class Helper permet donc ici d'ajouter ces membres manquants afin de préserver la compatibilité du code existant.

 
Sélectionnez
 ExceptionHelper = class helper (TObjectHelper) for Exception
  private
    class function CreateMsg(const Msg: string): Exception;
    function GetHelpContext: Integer;
    procedure SetHelpContext(AHelpContext: Integer);
  public
    /// Doc: The help context return zero(0) if exception's helplink property cannot be parsed into an integer.
    property HelpContext: Integer read GetHelpContext write SetHelpContext;

    // constructor Create(const Msg: string) is provided by the CLR class
    class function CreateFmt(const Msg: string; const Args: array of const): Exception;
    class function CreateHelp(const Msg: string; AHelpContext: Integer): Exception;
    class function CreateFmtHelp(const Msg: string; const Args: array of const;
      AHelpContext: Integer): Exception;

    // System.Exception.HResult is protected.  This helper exposes
    // the HResult for read-only access by non-descendents
    function HResult: Integer;
  end;
  ExceptionClass = class of Exception;

précédentsommairesuivant

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2005 Laurent Dardenne. Aucune reproduction, même partielle, ne peut être faite de ce site ni de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.