Conversion du langage C# vers Delphi 2005 pour .NET

Laurent Dardenne

Vous trouverez dans cet article des indications de conversion du langage C# vers Delphi 2005 pour .NET. ♪

- Titre : Conversion du langage C# vers Delphi 2005 pour .NET

- Auteur : Laurent Dardenne

- Parution : 8 février 2004

- Mise à jour : 23 juillet 2005

- Licence : 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 et 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.

Sommaire

- I. Public concerné

- II. Introduction

- III. Modificateur de types et de membres de types

- III-A. Modificateurs d'accès

- III-B. Autres modificateurs

- III-B-1. Exemple de procédure external

- III-B-2. Exemple d'initialisation de champ statique

- IV. Opérateurs

- V. Types

- VI. Tests

- VI-A. ?:

- VI-B. switch

- VI-C. Test de validité sur des pointeurs (null)

- VII. Structures itératives

- VII-A. While

- VII-B. Do/While

- VII-C. For

- VII-D. Foreach

- VII-E. Rupture de séquence

- VII-E-1. Break

- VII-E-2. Continue

- VII-E-3. Exit

- VIII. Exception

- VIII-A. Déclencher une exception

- VIII-B. Gestion d'une exception

- VIII-C. Redéclencher une exception

- VIII-D. Bloc finally

- IX. Classe

- IX-A. Héritage

- IX-B. Constructeur

- IX-C. Destructeur

- IX-D. Type de membres

- IX-E. Méthode

- IX-E-1. Procédure

- IX-E-2. Fonction

- IX-E-2-a. Fonction delegate

- IX-E-2-b. Gestionnaire d'événement

- IX-F. Passage de paramètres

- IX-G. Paramètres en nombre variable

- X. Propriétés

- X-A. Comparaison entre les propriétés et les indexeurs

- XI. Structure de données

- XI-A. Struct

- XI-B. Tableau

- XI-B-1. Tableau de tableaux (jagged array)

- XII. Déclaration de variables

- XIII. Instructions diverses

- XIII-A. Utilisation de mot clé réservé

- XIV. Attributs

- XV. C# 2.0 et les génériques…

I - Public concerné

Testé sous Dot Net 1.1 avec les spécifications du C# v1.2 et Delphi 2005.
Version 1.0

Je tiens à remercier Nono40, Pascal Jankowski et Sébastien Doeraene (sjrd) pour leurs relectures et suggestions pertinentes ainsi que pour leurs corrections orthographiques.

Cet article utilise quelques URL locales vers la documentation de .NET de Microsoft et de Delphi 2005.

II - Introduction

Étant en phase d'apprentissage sur le langage Delphi 2005 pour .NET, j'ai très vite compris que la conversion des exemples issus du SDK allait poser quelques problèmes aux nombreux débutants.
Cette approche peut paraître paradoxale, mais la compréhension du langage C# m'a, tout compte fait, rendu service même si cela a été un peu délicat dans les premiers temps. Il ne s'agit pas ici d'un cours complet sur le C# ni sur Delphi pour .NET, mais d'une suite d'indications. Le contexte pouvant influencer la conversion d'un code C# vers Delphi pour .NET.

Il va sans dire que cet article n'est pas exhaustif sur le sujet et sera mis à jour régulièrement, vos remarques et propositions y contribueront.

Concernant le C#, j'ai le plus souvent utilisé la terminologie propre au SDK.
Le symbole NI signifie fonctionnalité non implémentée en Delphi pour .Net.

Attention, certaines possibilités du langage C# ne sont pas compatibles avec la CLS.
Extrait du SDK :

Pour interagir entièrement avec d'autres objets, quel que soit le langage dans lequel ils ont été implémentés, les objets ne doivent exposer aux appelants que les fonctionnalités qui sont communes à tous les langages avec lesquels ils doivent fonctionner. Pour cette raison, un ensemble de fonctionnalités de langage appelé spécification CLS (Common Language Specification), qui comprend les fonctionnalités de langage de base nécessaires à de nombreuses applications, a été défini.

Il est donc conseillé de n'utiliser que le C# CLS Compliant. L'ajout de l'attribut personnalisé CLSCompliant indique au compilateur de vérifier si le code généré respecte bien les spécifications de la CLS.
Extrait du SDK :

Afin qu'un module soit conforme CLS, via [module:System.CLCSompliant(true)], il doit être généré avec l'option
/target:module du compilateur.

Pour ceux qui souhaiteraient aborder plus en détail la comparaison entre le langage C# et le langage Delphi pour .NET, consultez le cours complet sur le C# de RM di Scala .

Vous trouverez ici quelques liens utiles glanés au cours de ma rédaction qui vous permettront d'aborder plus précisément certains points :

Anders Hejlsberg : une courte biographie d'un des concepteurs du C# et de Delphi.

The C# Design Process : A Conversation with Anders Hejlsberg.

Pages Microsoft dédiées au langage C# notamment les spécifications 2.0.

Un cours complet sur le C# par RM di Scala.

Un second cours complet sur le C# (lien direct sur un fichier au format Pdf).

Conversion C# vers Delphi : un outil Borland en version BETA, sa version en add-in pour l'IDE Delphi 2005.

Conversion C++ vers C#

III - Modificateur de types et de membres de types

Ils permettent de modifier les déclarations des types et membres de types.

III-A - Modificateurs d'accès

Le compilateur Delphi pour .NET prend en charge des paramètres de visibilité supplémentaires conformes à la spécification CLS (Common Language Specification) .NET.

C#

Delphi .NET

Commentaire

public

public

C# : L'accès n'est pas limité.

Delphi .Net : Un membre public est visible partout où la classe peut être référencée.

protected

protected

C# : L'accès est restreint à la classe conteneur ou aux types dérivés de la classe conteneur.

Delphi .Net : Un membre protégé est visible partout dans le module où la classe est déclarée et dans toute classe descendante, indépendamment du module où la classe descendante est définie.
Le spécificateur de visibilité protégée de Delphi correspond à la visibilité assemblage ou famille du CLR.

protected internal

strict protected

C# : L'accès est restreint à l'assembly en cours ou aux types dérivés de la classe conteneur.

Delphi .Net : Les membres de classes dont la visibilité est privée stricte (strict protected) ne sont accessibles que dans la classe dans laquelle ils sont déclarés. Ils ne sont pas visibles pour les procédures ou fonctions déclarées dans la même unité.

private

private

C# : L'accès est restreint au type conteneur.
Les membres privés sont accessibles uniquement dans le corps de la classe ou du struct où ils sont déclarés. Les types imbriqués dans le même corps peuvent également accéder à ces membres privés.

Delphi .Net : Un membre privé est invisible hors de l'unité ou du programme dans lequel la classe est déclarée.
Le spécificateur de visibilité privée traditionnelle de Delphi correspond à la visibilité assemblage du CLR.

internal

strict private

C# : L'accès est restreint à l'assembly en cours. Les membres internes sont accessibles seulement dans les fichiers du même assembly.

Delphi .Net : Les membres de classes dont la visibilité est privée stricte (strict private) ne sont accessibles que dans la classe dans laquelle ils sont déclarés. Ils ne sont pas visibles pour les procédures ou fonctions déclarées dans la même unité.

Par défaut dans une classe en C# tous les membres sans qualificateur d'accès sont considérés comme privés (private).

Les types imbriqués, qui sont membres d'autres types, peuvent disposer d'accessibilités déclarées, voir « Guide de référence du programmeur C# » proposé dans l'aide de Delphi 2005.

À noter sous Delphi .Net :
Published : accessible à tout le code et depuis l'inspecteur d'objets ;
Automated n'est plus supporté en .NET.

III-B - Autres modificateurs

C#

Delphi .NET

Commentaire

static

class

C# : Utilisez le modificateur static pour déclarer un membre statique, qui appartient au type lui-même plutôt qu'à un objet spécifique.

Delphi .Net : Référence de classe ou méthode de classe. Les constructeurs de classe sont également possibles.

static extern

external

C# : Les méthodes externes sont implémentées en externe, en utilisant généralement un langage autre que C#.

virtual

virtual

C# : Lorsqu'une déclaration de méthode d'instance comprend un modificateur virtual, cette méthode est une méthode virtuelle.
Dans un appel de méthode virtuelle, le type au moment de l'exécution de l'instance pour lequel l'appel intervient détermine l'implémentation de la méthode réelle à appeler.
Déclarer une méthode ou un accesseur dont l'implémentation peut être modifiée par un membre de redéfinition dans une classe dérivée.

abstract

abstract

C# : Le modificateur abstract est utilisé pour une méthode ou une propriété d'une classe qui n'a pas d'implémentation ou pour une classe qui comprend des méthodes de ce type.

Delphi .Net : Lorsqu'une méthode est déclarée abstract dans une classe ancêtre, vous devez la redéclarer et l'implémenter dans tout composant/classe descendante avant d'utiliser le nouveau composant/nouvelle classe.

overload

C# : La surcharge existe, mais il n'existe pas de mot clé associé. La liste d'arguments différents pour des méthodes de même nom détermine la surcharge.

Delphi .Net : Les membres de classes dont la visibilité est protégée stricte (strict private) sont visibles dans la classe dans laquelle ils sont déclarés et dans toutes les classes dérivées, quel que soit l'endroit où elles sont déclarées.
La redéfinition (ou surcharge)
Les routines redéfinies doivent être redéclarées avec la directive overload et doivent utiliser une liste d'arguments différents.

override

C# : Fournit une nouvelle implémentation d'un membre virtuel hérité d'une classe de base. Il n'existe pas de mot clé pour préciser la surcharge.

Delphi .Net : Surcharger une méthode signifie l'étendre ou la redéfinir plutôt que la remplacer. Une classe descendante peut surcharger toutes ses méthodes virtuelles héritées.

const

const

C# : Spécifier que la valeur du champ ou la variable locale ne peut pas être modifiée.

Delphi .Net : Les possibilités d'initialisation ne sont pas identiques.

readonly

NI

C# : Déclarer un champ auquel seules peuvent être attribuées des valeurs au sein de la déclaration ou dans un constructeur de la même classe.

Delphi .Net : Utiliser une propriété pour interdire l'accès. Si vous ne déclarez aucune méthode read, la propriété fonctionne uniquement en écriture.

sealed

sealed

C# : Spécifier qu'une classe ne peut pas être héritée.

unsafe

NI

C# : Déclarer un contexte non sécurisé.

Delphi .Net : Voir la directive de compilation {UNSAFECODE ON}.

volatile

NI

C# : Indiquer qu'un champ peut être modifié dans le programme par quelque chose tel que le système d'exploitation, le matériel ou un thread s'exécutant simultanément.

Delphi .Net : Variable de thread ?

III-B-1 - Exemple de procédure external

[DllImport("kernel32", SetLastError=true)]
static extern bool CreateDirectory(string name, SecurityAttribute sa);

[DllImport('user32.dll', SetLastError=true)]
function CreateDirectory(name: string ; sa : SecurityAttribute):Boolean; external;

III-B-2 - Exemple d'initialisation de champ statique

using System;
class Test
{
static void Main() {
Console.WriteLine("{0} {1}", B.Y, A.X);
}
public static int F(string s) {
Console.WriteLine(s);
return 1;
}
}
class A
{
public static int X = Test.F("Init A");
}

Dans ce cas sous Delphi, on utilisera une variable de classe :

A=Class
Public
// champs de classe, static
Class var X : Integer;
end;

Le code suivant qui contient du code dans la déclaration du type :

public static readonly DBBool dbNull = new DBBool(0);

est convertie en utilisant un constructeur de classe.

TDBBool = record
Strict private
// Champ privé pour stocker la valeur du type
FValeur : integer;

// champs de classe, static
Class var FdbNull  : TDBBool;

Class Constructor CCreate;

Private
// Le constructeur ne doit pas être accessible
// Private pour TDBBool.dbTrue:=TDBBool.Create(0);
Constructor Create(AValeur: DBType);
...
end;

implementation

Constructor TDBBool.Create(AValeur: DBType);
Begin
FValeur:=AValeur;
end;

Class Constructor TDBBool.CCreate;
Begin
// Initialise les valeurs possibles du type
TDBBool.FdbNull.FValeur:=cdbNull;
end;

Vous trouverez ici la conversion du code complet de cet exemple de classe en C#.

IV - Opérateurs

Catégorie d'opérateurs

C#

Delphi .NET

Commentaire

Arithmétique

+ , - , * , / , % (modulo)

+ , - , * , / et div , mod

C# :

Delphi .Net : / pour une division réelle, et div pour une divisons entière

Logique binaire

&, |, ^, !, ~

AND, OR, XOR, NOT, NOT

C# : L'opérateur ~ effectue une opération de complément de bits sur son opérande. Les opérateurs de complément de bits sont prédéfinis pour int, uint, long et ulong.

! est l'opérateur de négation logique

Logique booléen

&&, ||, true, false

AND, OR, TRUE, FALSE

Concaténation de chaînes

+

+

Incrément, décrément

++, --

Inc(), Dec()

Déplacement

<<, >>

Shl, Shr

L'opérateur de déplacement vers la gauche (<<) ou la droite (>>) déplace le premier opérande vers la gauche ou la droite du nombre de bits spécifié par le second opérande.

Relationnel

==, !=, <, >, <=, >=

=, <>, <, >, <=, >=

C# :

Delphi .Net : Les opérateurs relationnels =, <>, <, >, <= et >= acceptent tous des opérandes chaîne. Les opérateurs relationnels = et <> opèrent également sur des classes.

Assignation

=, +=, -=, *=, /=, %=, &=, |=, ^=, <<=, >>=

:=, Inc(), Dec(), NI, idem,…

C# : Une expression de la forme x ^= y
est évaluée comme x = x ^ y

Delphi .Net : Les assignations non implémentées se font en deux passes ou dans la même instruction d'assignation.

Accès au membre

. , this

. , Self

L'opérateur point (.) est utilisé pour l'accès aux membres.

Indexation

[]

[]

Cast

datatype()

datatype()

Conditionnel

?:

If Then Else

L'opérateur conditionnel est associatif à droite.

Concaténation et suppression de délégué

+, -

Include, Exclude

Création d'objets

new

Create

voir aussi le chapitre : Structure de données-tableaux

Informations de type

as, is, sizeof, typeof

AS, IS, SIZEOF, NI

C# : L'opérateur typeof permet d'obtenir l'objet System.Type pour un type. Il peut être remplacé par l'appel de la méthode GetType.

Delphi .Net : Appliquer la fonction TypeOf standard à un type ne possédant pas de fonction virtuelle provoquera une erreur

Contrôle des exceptions de dépassement de capacité

checked, unchecked

Directives de compilation

C# : Dans un contexte non vérifié (unchecked), si une expression produit une valeur qui est hors de la plage du type de destination, le résultat est tronqué.
Delphi .Net : Directive de compilation $Q: Contrôle la génération de code qui teste le dépassement de capacité. voir aussi $R

Adresse et adressage indirect

*, ->, [], &

Pointer, Pointer^.x, incrément d'adresse,@

Il s'agit d'instructions spécifiques à Win32, générant du code non sécurisé.
Sous Delphi .Net voir la directive UNSAFECODE

Ensemble

enum

Set of type

C# : Voir les méthodes de la classe enum

Delphi .Net :
Ens1 = Set of 1..10;
Ens1 := [1, 3, 5, 7, 9];
Les opérateurs suivants [+, -, *, <=, >=, =, <>, in] acceptent des ensembles comme opérandes.

La surcharge d'opérateurs en C# :

public static implicit operator DBBool(bool x)

La surcharge d'opérateurs en Delphi .Net :

Class operator TDBBool.Explicit(x: TDBBool): Boolean;

Les opérateurs true et false du C# ne peuvent être surchargés sous Delphi .NET.

V - Types

Les types sont communs aux deux langages s'ils sont 'mappés' sur les types de System.xxx.
Sous Delphi 2005 pour .Net certains types sont des alias et d'autres des simulations.

Soyez attentif au fait que le comportement d'un code C#, particulièrement au niveau des types, peut dépendre :

- de directives de compilation, notamment /checked ;

- de convention de conversion, implicite ou explicite, différente.

VI - Tests

VI-A - ?:

L'expression évaluée doit avoir un résultat booléen en C#.

a ? b : c ? d : e

If a=true then b
else if c=true then d
else e

VI-B - switch

L'instruction de contrôle switch est convertie en case of. La partie default est convertie en else. En C# l'instruction break indique la fin de la branche case, s'il n'y a pas de break l'exécution continue au cas suivant.

public override string ToString()
{
switch (value)
{
case -4
case -3
case -2:
Console.writeline("Exemple");
Break;
case -1:
return "DBBool.False";
case 0:
return "DBBool.Null";
case 1:
return "DBBool.True";
default:
throw new InvalidOperationException();
}
}

Function TDBBool.ToString: String;
begin
Case Fvaleur of
-4,
-3,
-2: begin
Console.writeline("Exemple");
end;

-1: begin
Result:='TDBBool.False';
Exit;
end;
0: begin
Result:='TDBBool.Null';
Exit;
end;
1: begin
Result:='TDBBool.True';
Exit;
end;
else Raise InvalidOperationException.Create;
end;
end;

Dans ce dernier exemple Delphi, l'instruction Exit est nécessaire pour refléter exactement l'instruction return contenue dans le code C#.

VI-C - Test de validité sur des pointeurs (null)

if (EventTest != null)
EventTest(this, new EventArgs());

Attention sous Delphi NULL est une fonction de Borland.Vcl.Variants permettant d'obtenir un variant Null.
Utilisez la constante prédéfinie nil.

Toute variable procédurale peut contenir la valeur nil, ce qui signifie qu'elle ne pointe sur rien. Pour tester si une variable procédurale est initialisée, utilisez la fonction standard Assigned

if Assigned(FEventTest) then
FEventTest(Self, EventArgs.Create);

VII - Structures itératives

VII-A - While

public static void Main()
{
int n = 1;
while (n < 6) do
{
Console.WriteLine("la valeur courante de n est {0}", n);
n++;
}
}

procedure Main;
var n: integer;
begin
n:= 1;
while (n < 6)
begin
WriteLn('la valeur courante de n est ', n);
inc(n);
end;
end;

VII-B - Do/While

public static void Main ()
{
int x;
int y = 0;

do
{
x = y++; // post-incrémentation
Console.WriteLine(x);
}
while(y < 5);
}

procedure Main;
var x, y : integer;
begin
y:=0;

Repeat
x:=y;
inc(y);
Console.WriteLine(x);
Until (y >= 5);
end;

Ici prenez garde à l'inversion du test.

VII-C - For

for (int i = 1; i <= 10; i++)
{
Console.WriteLine(i);
}

Var i: integer;
Begin
for i := 1 to 10 do
begin
Console.WriteLine(i);
end;

VII-D - Foreach

// Utilisation avec les tableaux
using System;
class MainClass
{
public static void Main()
{
int odd = 0, even = 0;
int[] arr = new int [] {0,1,2,5,7,8,11};

foreach (int i in arr)
{
if (i%2 == 0)
even++;
else
odd++;
}
Console.WriteLine("Trouvé  {0} chiffre impair et {1} chiffre pair.",
odd, even) ;
}
}

Sous Delphi .NET on utilise un tableau multidimensionnel alloué dynamiquement.

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
if (i mod 2 = 0)
then inc(even)
else inc(odd);

WriteLn('Trouvé '+ intToStr(odd)+' chiffre impair et '+intToStr(even)+' chiffre pair.') ;
Readln;
end;

Delphi pour .NET prend en charge l'itération du style for-element-in-collection sur les conteneurs. Les modèles suivants d'itérations sur les conteneurs sont reconnus par le compilateur :

- for Element in ArrayExpr do Instruction ;

- for Element in StringExpr do Instruction ;

- for Element in SetExpr do Instruction ;

- for Element in CollectionExpr do Instruction.

Pour utiliser la construction de boucle for-in sur une classe, cette dernière doit implémenter un modèle de collection prescrit. Consulter la documentation pour les détails de l'implémentation.

VII-E - Rupture de séquence

VII-E-1 - Break

Comportement identique
C# : l'instruction 'break' termine la boucle englobante la plus proche où l'instruction switch apparaît.

Delphi .Net : la procédure 'break' fait quitter le flux de contrôle d'une instruction for, while ou repeat et passe à la prochaine instruction qui suit l'instruction de boucle.

VII-E-2 - Continue

Comportement identique
C# : L'instruction 'continue' transmet le contrôle à l'itération suivante de l'instruction d'itération englobante où elle apparaît.

Delphi .Net : La procédure 'continue' poursuit le flux de contrôle jusqu'à la prochaine itération d'une instruction for, while ou repeat.

VII-E-3 - Exit

Attention homonymie et comportement différent
C# : Termine le processus et donne au système d'exploitation sous-jacent le code de sortie spécifié.

Delphi .Net : La procédure 'Exit' fait immédiatement passer le contrôle d'exécution en dehors de la procédure. Si la procédure en cours correspond au programme principal, Exit termine l'exécution du programme.

VIII - Exception

VIII-A - Déclencher une exception

public static explicit operator bool(DBBool x)
{
if (x.value == 0) throw new InvalidOperationException();
return x.value > 0;
}

Class operator TDBBool.Explicit(x: TDBBool): Boolean;
const
cdbNull=0;
cdbTrue=1;

begin
If x.FValeur=cdbNull
then raise InvalidOperationException.Create
else Result:=x.FValeur=cdbTrue;
end;

VIII-B - Gestion d'une exception

public override bool Equals(object o)
{
try
{
return (bool) (this == (DBBool) o);
}
catch
{
return false;
}
}

Function TDBBool.Equals(x: TObject):Boolean;
begin
try
Result:= (Self = x as TDBBool);
except
Result:=False;
end;
end;

VIII-C - Redéclencher une exception

Une instruction throw peut être utilisée dans le bloc catch pour lever une nouvelle fois l'exception qui a été interceptée par l'instruction catch.

Par exemple :

try
...
catch (InvalidCastException e)
{
Console.WriteLine("b est NULL")
throw (e);   // Redéclenche l'exception e avec ses paramètres
}

try
...
except
On E:InvalidOperationException do
begin
Console.WriteLine('b est NULL')
Raise; // L'exception concernée, ici E, est implicite
end;
end;

En C# on peut redéclencher une exception sans les paramètres : throw.
En Delphi .Net cette possibilité n'existe pas.

VIII-D - Bloc finally

public static void Main()
{
int i = 123;
string s = "Une chaîne";
object o = s;

try
{
// conversion invalide ; o contient une string et pas un entier.
i = (int) o;
}
finally
{
Console.Write("i = {0}", i);
}
}

Procedure Main()
var
i: integer;
s: string;
o: TObject;

begin
i:= 123;
s:='Une chaîne';
o:=s;

try
// conversion invalide ; o contient une string et pas un entier.
i = integer (o);
finally
Console.Write('i = {0}', i);
end;
end;

IX - Classe

IX-A - Héritage

C#

Delphi .NET

Commentaire

class TopEvent : BaseEvent {…

TForm1 = class(TForm) …

IX-B - Constructeur

Le constructeur porte le même nom que la classe et en respectant la casse.

L'utilisation du mot clé this dans un constructeur indique l'appel du constructeur par défaut :

...
public Initialise(Int32 n): this(){
this.n = n;
}
...

...
constructor Initialise(n : integer);
begin
inherited Create; // En Delphi .NET l'appel de l'ancêtre est obligatoire.
Self.n = n;
end;
...

IX-C - Destructeur

La syntaxe suivante déclare un destructeur :

public class test{
...
~Test(){
// Cleanup statements.
CloseHandle(Handle);
}
...
}

Identique au code :

public class test{
...

protected override void Finalize()
{
try
{
// Cleanup statements.
CloseHandle(Handle);

}
finally
{
base.Finalize();
}
}

Le mot clé base est utilisé pour accéder aux membres de la classe de base à partir d'une classe dérivée et correspond au mot clé inherited sous Delphi.
Le code C# précédent correspond au code Delphi .NET suivant :

procedure TTest.Finalize;
begin
try
CloseHandle(handle.ToInt32);
finally
inherited;
end
end;

IX-D - Type de membres

Les déclarations de classe peuvent contenir des déclarations pour des

- constantes ;

- champs ;

- méthodes ;

- propriétés ;

- événements ;

- indexeurs ;

- opérateurs ;

- constructeurs d'instance ;

- constructeurs statiques ;

- destructeurs ;

- types imbriqués.

L'initialisation de membres, dans la partie déclaration d'une classe, n'est pas possible sous Delphi .Net.

IX-E - Méthode

IX-E-1 - Procédure

En C# le mot clé void indique une fonction ne renvoyant aucun résultat ce qui est le cas d'une procédure sous Delphi .NET.

void UneMethode();

Procedure UneMethode;

IX-E-2 - Fonction

Le mot clé return est remplacé par la combinaison de Result et d'Exit.

static double CalculateArea(int r)
{
double area;
area = r*r*Math.PI;
return area;
}

function CalculateArea(r: integer):double;
var area : double;
begin
area:= r*r*Math.PI;
Result:=area;
Exit;
end;

IX-E-2-a - Fonction delegate

En C#

delegate void MethodeDelegate(int i);

En Delphi .NET

MethodeDelegate = function(x: Integer): string of object;

IX-E-2-b - Gestionnaire d'événement

En C#

public delegate void EventHandler(Object sender, EventArgs e);

En Delphi .NET

TEventHandler = Procedure (Sender:TObject ; e : EventArgs) Of Object;

La composition de délégué en C# se fait avec les opérateurs suivants += et -=
La composition de délégué Delphi .NET se fait de la manière suivante :

TCompte=Class
...
// Utilisation d'une propriété dans une classe
Property MonDelegue:TMonDelegue add FMonDelegue remove FMonDelegue;
...
end;

...

// L'ajout d'un délégué
Include(UnCompte.MonDelegue,@Traite);
...
// La suppression d'un délégué
Exclude(UnCompte.MonDelegue,@Traite);
...

IX-F - Passage de paramètres

C#

Delphi .NET

Commentaire

int a

a:integer

Passage par valeur

C# : Si le paramètre est une référence, il est possible de le modifier. Toutefois, 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.

Delphi .Net : idem

ref

Var

C# : Toute modification apportée au paramètre dans la méthode est reflétée dans cette variable lorsque la méthode appelante récupère le contrôle.

Delphi .Net :

const

Const

out

Out

C# : Toute modification apportée au paramètre dans la méthode est reflétée dans cette variable lorsque la méthode appelante récupère le contrôle.
Une variable passée en tant qu'argument out n'a pas besoin d'être initialisée. Toutefois, une valeur doit être attribuée au paramètre out avant le retour de la méthode.

Delphi .Net :

Valeur par défaut

identifiant=valeur

C# : TODO

Delphi .Net : TODO

Voir aussi:

Passage de paramètres en C#

IX-G - Paramètres en nombre variable

Sous C# un tableau de paramètres est déclaré avec un modificateur params. Il ne peut exister qu'un tableau de paramètres pour une méthode donnée, et celui-ci doit être le dernier paramètre spécifié. Le type d'un tableau de paramètres est toujours un type tableau unidimensionnel.

public class Console
{
public static void Write(string fmt, params object[] args) {...}
public static void WriteLine(string fmt, params object[] args) {...}
...
}

Sous Delphi .Net ce type ne nécessite pas l'utilisation d'un mot clé, on le déclare par un paramètre tableau ouvert contenant des TObjet.

Procedure(fmt: string; Args : array of TObject);

Vous pouvez consulter la FAQ Delphi .NET : Comment passer un nombre variable de paramètres à une procédure ?

X - Propriétés

Les modificateurs autorisés sont new, static, virtual, abstract, override et une combinaison valide des quatre modificateurs d'accès (public…).

using System;
class Person
{
private int myAge = 0;

// Déclare une propriété Age de type integer:
public int Age
{   // Déclare les méthodes accesseur.
get
{
return myAge;
}
set
{
myAge = value;
}
}
}

En C# la déclaration de méthodes accesseurs (Set et Get) est obligatoire. La variable value est ajoutée dans ce cas par le compilateur.

En Delphi .NET elles sont créées par le compilateur si on référence directement une variable.
Propriété en read-only :

// Area est une propriété en read-only.
public abstract double Area
{
get;
}

Surcharge de propriété :

// Redéclaration de la propriété Area dans une classe dérivée
public override double Area
{
get
{
return mySide * mySide;
}
}

Sous Delphi pour .NET seules sont supportées les propriétés de type suivant :

- Simple ;

- Énuméré ;

- Ensemble ;

- Objet ;

- Tableau.

Déclarer une valeur par défaut pour une propriété n'a pas pour effet de définir cette propriété par cette valeur.

X-A - Comparaison entre les propriétés et les indexeurs

TODO : comparaison
Les indexeurs sont similaires aux propriétés. À l'exception des différences répertoriées dans le tableau ci-dessous, toutes les règles définies pour les accesseurs des propriétés s'appliquent aux accesseurs des indexeurs.

Propriété

Indexeur

Delphi .Net

Identifiée par son nom.

Identifié par sa signature.

Accès par le biais d'un nom simple ou de l'accès à un membre.

Accès par le biais de l'accès à un élément.

Peut être un membre statique ou un membre d'instance.

Doit être un membre d'instance.

Un accesseur get d'une propriété n'a aucun paramètre.

Un accesseur get d'un indexeur possède la même liste de paramètres formels que l'indexeur.

Un accesseur set d'une propriété contient le paramètre value implicite.

Un accesseur set d'un indexeur possède la même liste de paramètres formels que l'indexeur, outre le paramètre value.

XI - Structure de données

XI-A - Struct

public struct Point
{
public int x, y;

public Point(int p1, int p2)
{
x = p1;
y = p2;
}
}

La construction d'une structure (struct en C#) se fait en 2 étapes sous Delphi .Net.
La première dans la partie Interface.

Type
Point = Record
x, y : integer;

constructor Point.Create(p1,p2 : integer) ;
end;

et la seconde dans la partie implementation.

constructor Point.Create(p1,p2 : integer) ;
begin
x:=p1;
y:=p2;
end;
end;

XI-B - Tableau

Les tableaux sont des objets et peuvent être de type unidimensionnel, multidimensionnel et des tableaux de tableaux (jagged array).

int[] arr = new int[5];
arr[0] = 2;

var
arr : array[0..4] of integer; // tableau statique
begin
arr[0]:= 2;

Utilisation d'un initialiseur de tableau

int[] arr = new int[3] {0, 1, 2};

arr:= New(array[] of integer,(0,1,2)); ...

XI-B-1 - Tableau de tableaux (jagged array)

// tableau de tableaux (jagged array)
byte[][] scores = new byte[5][];
// Création du "jagged array"
for (int i = 0; i < scores.Length; i++)
{
scores[i] = new byte[i+3];
}

type
TScores = Array of Array of byte; // Tableaux dynamiques multidimensionnels

var Scores :TScores;
i: integer;
begin
SetLength(Scores,10); // Allocation des lignes
// Création du "jagged array"
for i := Low(Scores) to High(Scores) do
SetLength(cores[i], i+3); // Allocation des colonnes

Le terme jagged peut être traduit par déchiquetée, mais irrégulier semble plus approprié. Jag voulant signifier déchiré (saoul) :*)

XII - Déclaration de variables

Les variables en C# se déclarent tout de suite après la déclaration du nom de classe.

class ExempleVisible2 {
int a = 10; // Cette initialisation est déclarée dans le constructeur.
int g (int x )
{ return 3*x-a;
}

ExempleVisible2=Class
public
a : integer;
Function g(x : integer):Integer;
Constructor Create;
end;
// L'assignation de a = 10 du C# est à la charge du développeur en Delphi .NET.
Constructor Create;
begin
a:=10;
end;

En C# on peut aussi déclarer des variables 'à la volée' directement le code, mais cette possibilité n'est pas supportée sous Delphi pour .NET, ce langage restant déclaratif.

// Extrait de l'unité System.pas
...
var
LMainThread: System.Threading.Thread;

function MainThread: System.Threading.Thread;
begin
Result := LMainThread;
end;

var
LastRandSeed: Integer = -1;
RandomEngine: System.Random;

procedure InitRandom;
begin
...

XIII - Instructions diverses

C#

Delphi .NET

Commentaire

using

uses

Import de types définis dans d'autres espaces de noms, dans ce contexte le mot clé using est converti en uses .

Alias using

NI.

C# : Créer un alias pour un espace de noms (un alias using).
using MyAlias = MyCompany.Proj.Nested;

Delphi .Net : Vous pouvez par contre utiliser l'alias de types (type et classe), vous n'aurez pas à qualifier complètement le nom.
Dans ce cas le mot clé using est converti en uses

fixed

GCHandle

C# :
// must use fixed to get address of pt.x and pin pt in place while we use the pointer
fixed ( int* p = &pt.x ){
*p = 1;
}

Delphi .Net : cf. System.Runtime.InteropServices.GCHandle
Attention pour développeurs confirmés !

lock()

Monitor

C# : lock(directory) {…}

Delphi .Net :

Monitor.Enter(directory);
try
…
finally
Monitor.Exit(directory);

//

//

La déclaration des commentaires est identique pour // et /* */.

P.x = P.y= 10;

P.x:=10;
P.y:=10;

Assignation multiple.

123.ToString

Integer(123).ToString

Delphi .Net : L'utilisation de méthode sur des types primitifs nécessite un transtypage.

Notez la seconde possibilité d'utilisation du mot clé using en C#:
L'instruction using définit une portée au bout de laquelle un objet est supprimé.

using (Object MonObjet= new Object)
{
MonObjet.FaitqqChose;
}

En Delphi .Net ce mot clé peut être remplacé par la construction With do suivante :

with MonObjet.Create do
try
MonObjet.FaitqqChose;
finally
if Assigned(MonObjet) then
(MonObjet as IDisposable).Dispose;
end;

L'objet que vous instanciez doit implémenter l'interface System.IDisposable.

XIII-A - Utilisation de mot clé réservé

public FileInfo lockFileInfo;

public override bool Obtain()
{
try
{
FileStream fs = lockFileInfo.Create();
fs.Close();
}
catch
{
return false;
}
return true;
}

var
fs: FileStream;
begin
try
fs := lockFileInfo.Create('');

Provoque l'erreur :

[Erreur] Project1.dpr(18): E2382 Impossible d'appeler des constructeurs utilisant des variables d'instance

Le mot Create étant un mot clé réservé sous Delphi pour créer une nouvelle instance. Dans ce cas il faut utiliser le caractère éperluet &.

var
fs: FileStream;
lockFileInfo: FileInfo;

Begin
try
lockFileInfo := FileInfo.Create('MyFile.txt');
fs := lockFileInfo.&Create;
except
fs.Close();
end;

XIV - Attributs

Les attributs sont identiques aux deux langages. Seule leur déclaration, cf. classe, diffère.
En C# la convention veut que l'on suffixe, lors de la déclaration, le nom de la classe avec Attribut, mais pas dans l'utilisation (le compilateur C# se chargeant d'ajouter le suffixe).
Certains attributs, par exemple conditionnal, sont spécifiques au compilateur C# de Microsoft.

XV - C# 2.0 et les génériques…

C#, les possibles évolutions

Extrait d'un interview de Danny Thorpe au sujet de Delphi 2005 :
Microsoft has .NET 2.0 announced for the next year. When can we expect Delphi 10 and what have you planned for it ?
Probably the biggest item in terms of the compiler/language is implementing parameterized type syntax (generic types) in Delphi. We've had parameterized type syntax sketched out on the whiteboards here for ages but other stuff (platforms) kept taking priority. The general goal is to have a product release in 2005, shortly after .NET 2.0 is finalized and released.

C# 3.0 (Comega) la fusion de la POO du SQL et de XML ?

Exemple :

{
Database db = new Database("db://amazon.com");
XML list = db.Search("Author like 'Hugh Darwen'");
foreach (XML book in list) {
wishlist.Add(book.Title + ", " + book.ISBN);
}