1. Public concerné

Image non disponible


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

Je tiens a 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.

2. 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é en Delphi pour .Net.



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

 
Sélectionnez

        
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 :

 
Sélectionnez

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étails 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#

3. Modificateur de types et de membres de types

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

3-1. 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épendemment 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.

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

3-2. 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 contructeurs de classe sont également possible.
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és 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 ?

3-2-1. Exemple de procédure external

 
Sélectionnez

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

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

3-2-2. Exemple d'initialisation de champ statique

 
Sélectionnez

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 :

 
Sélectionnez

 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

 
Sélectionnez

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

est convertie en utilisant un constructeur de classe.

 
Sélectionnez

   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 cette exemple de classe en C#.

4. 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és 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# :

 
Sélectionnez

public static implicit operator DBBool(bool x) 

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

 
Sélectionnez

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

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

5. 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 niveaux des types, peut dépendre :

  • de directives de compilation, notamment /checked.
  • de convention de conversion, implicite ou explicite, différente.

6. Tests

6-1. ?:

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

 
Sélectionnez

a ? b : c ? d : e
 
Sélectionnez

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

6-2. 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.

 
Sélectionnez

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();
      }
  }
 
Sélectionnez

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#.

6-3. Test de validité sur des pointeurs (null)

 
Sélectionnez

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

 
Sélectionnez

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

7. Structures itératives

7-1. While

 
Sélectionnez

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

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

7-2. Do/While

 
Sélectionnez

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

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

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.

7-3. For

 
Sélectionnez

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

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

7-4. Foreach

 
Sélectionnez

// 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.

 
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
  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.

7-5. Rupture de séquence

7-5-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.

7-5-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.

7-5-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.

8. Exception

8-1. Déclencher une exception

 
Sélectionnez

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

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;

8-2. Gestion d'une exception

 
Sélectionnez

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

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

8-3. 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 :

 
Sélectionnez

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

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.

8-4. Bloc finally

 
Sélectionnez

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);
   }         
}
 
Sélectionnez

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;

9. Classe

9-1. Héritage

C# Delphi .NET Commentaire
class TopEvent : BaseEvent {... TForm1 = class(TForm) ...  

9-2. 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 :

 
Sélectionnez

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

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

9-3. Destructeur

La syntaxe suivante déclare un destructeur :

 
Sélectionnez

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

Identique au code :

 
Sélectionnez

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 :

 
Sélectionnez

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

9-4. 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.

9-5. Méthode

9-5-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.

 
Sélectionnez

void UneMethode();
 
Sélectionnez

Procedure UneMethode;

9-5-2. Fonction

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

 
Sélectionnez

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

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

9-5-2-1. Fonction delegate

En C#
 
Sélectionnez

delegate void MethodeDelegate(int i);
En Delphi .NET
 
Sélectionnez

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

9-5-2-2. Gestionnaire d'événement

En C#
 
Sélectionnez

public delegate void EventHandler(Object sender, EventArgs e);
En Delphi .NET
 
Sélectionnez

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 avec de la maniére suivante :

 
Sélectionnez

 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);
...

9-6. 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#

9-7. 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.

 
Sélectionnez

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.

 
Sélectionnez

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 ?

10. Propriétés

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

 
Sélectionnez

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 :

 
Sélectionnez

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

Surcharge de propriété :

 
Sélectionnez

 // 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
  • Enumé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.

10-1. 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.  

11. Structure de données

11-1. Struct

 
Sélectionnez

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.

 
Sélectionnez

Type
 Point = Record
  x, y : integer;

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

et la seconde dans la partie implementation.

 
Sélectionnez

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

11-2. Tableau

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

 
Sélectionnez

 int[] arr = new int[5];
 arr[0] = 2;
 
Sélectionnez

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

Utilisation d'un initialiseur de tableau

 
Sélectionnez

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

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

11-2-1. Tableau de tableaux (jagged array)

 
Sélectionnez

    // 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];
   }
 
Sélectionnez

type
 TScores = Array of Array of byte; // Tableaux dynamiques multi-dimensionnels

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) :*)

12. Déclaration de variables

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

 
Sélectionnez

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

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.

 
Sélectionnez

 // 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
...

13. 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 primitif 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é.

 
Sélectionnez

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

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

 
Sélectionnez

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.

13-1. Utilisation de mot-clé réservé

 
Sélectionnez

   public FileInfo lockFileInfo;

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

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

Provoque l'erreur :

 
Sélectionnez

[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 &.

 
Sélectionnez

var
  fs: FileStream;
  lockFileInfo: FileInfo;

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

14. Attributs

Les attributs sont identiques aux 2 langages. Seul leur déclaration, cf. classe, diffère.
En C# la convention veux 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.

15. 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 d'XML ?

Exemple :

 
Sélectionnez

{
  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);
  }