Migrer du C++ au C#

Leduke

Ce document a pour but de présenter les principales différences entre le C++ et le C# et d'aider les personnes désireuses de réaliser le portage d'une application C++ en C#.

Commentez ♪

- Titre : Migrer du C++ au C#

- Auteur : Leduke

- Parution : 27 novembre 2002

- Licence : L'ensemble ou partie de ce document ainsi que le code mis à disposition, ne peut être diffusé sur d'autres sites Web sans l'autorisation au préalable de son créateur. Certaines parties de cet article sont extraites de l'article : « C++ ? C# : What You Need to Know to Move from C++ to C# » de Jesse Liberty.

Sommaire

- I. Introduction

- I-A. 1. Pièges

- II. Les principales différences entre le C++ et le C#

- III. Les types de valeur et de référence

- IV. Les structures

- V. Tout dérive de la classe Object

- VI. La gestion des paramètres des prototypes de fonctions

- VII. Le mot clé « new »

- VIII. Les propriétés

- IX. Les tableaux

- X. Conclusion

- XI. Note de la rédaction de Developpez.com

I - Introduction

Le langage C# s'appuie sur la syntaxe et la sémantique du C++, permettant au programmeur C de bénéficier des avantages de .NET et de la Common Language Runtime. Alors que la transition du C++ vers le C# peut paraître aisée, il y a un certain nombre d'éléments qui ont changé que nous allons étudier tels que l'opérateur new, les structures, les constructeurs, les destructeurs.

I-A - 1. Pièges

Le langage C# ressemble à s'y méprendre au C++. Il se peut que vous écriviez du code qui est parfaitement correct en C++, mais qui ne compile pas en C# ou au pire ne se comporte pas à l'exécution comme vous l'attendiez. La plupart des différences syntaxiques entre le C++ et le C# sont triviales (pas de point-virgule après la déclaration d'une classe, Main comporte désormais une majuscule) et sont détectées par le compilateur.

II - Les principales différences entre le C++ et le C#

Fonctionnalité

Documentation de référence

Héritage : une classe peut hériter de l'implémentation d'une seule classe de base uniquement. Une classe ou une interface peuvent implémenter plusieurs interfaces.

class
interface

Tableaux : la déclaration d'un tableau en C# est différente de celle en C++. Les crochets [] doivent apparaître à la suite du type de tableau en C#.

Arrays

Le type bool : il n'existe pas de conversion entre le type bool et d'autres types (particulièrement int).

bool

Le type long : en C#, le type de données long est sur 64 bits alors qu'il est sur 32 bits en C++.

long

Le type struct : en C#, les classes et les structures sont sémantiquement différentes. Une structure est un type de valeur alors qu'une classe est un type de référence.

struct
class

L'instruction switch : comparativement au C++, le C# n'autorise pas le passage automatique à chaque condition d'une clause switch.

switch

Le type delegate : les delegates ressemblent à des pointeurs de fonctions, mais ils sont sécurisés et fortement typés.

delegate

Appel d'une fonction membre d'une classe surchargée à partir d'une classe dérivée.

base
Voir aussi les exemples pour override

Utilisation du mot clé New afin de cacher explicitement une fonction héritée.

new

La surcharge d'une méthode nécessite l'utilisation du mot clé override.

override

Les directives de préprocessing sont utilisées pour la compilation conditionnelle. Il n'y a pas de fichier includes en C#.

C# Preprocessor Directives

La gestion des exceptions : l'utilisation de la clause finally.

try-finally
try-catch-finally

Les opérateurs C# : le C# comporte des opérateurs supplémentaires tels que is et typeof ainsi que des fonctionnalités différentes de certains opérateurs logiques.

& Operator
| Operator
^ Operator
is
typeof

L'utilisation du mot clé extern.

extern

L'utilisation du mot clé static.

static

Une solution alternative à la liste d'initialisation C++ dans la construction d'une classe de base.

Voir les exemples pour virtual

La structure générale d'un programme C# : les espaces de noms, les classes, les structures, les delegates, les énumérations.

General Structure of a C# Program

La déclaration de la méthode Main diffère de celle en C++, mais aussi la manipulation des arguments passés en ligne de commande.

Main

Les paramètres des méthodes : le C# supporte les mots réservés ref et out, qui sont utilisés à la place de pointeurs pour le passage de paramètre par référence.

ref
out

Les pointeurs sont autorisés en C#, mais seulement dans un mode dit « unsafe ».

unsafe

La surcharge d'opérateurs est différente en C#.

C# Operators

Les chaînes de caractères C# sont différentes de celles en C++.

string

Le mot clé foreach permet de parcourir des tableaux et des collections.

foreach, in

Il n'existe pas de méthodes ou de variables globales en C# : les méthodes et les variables doivent être contenues dans la portée d'une déclaration (telle qu'une classe ou une structure).

General Structure of a C# Program

Il n'existe pas de fichiers entêtes ou de directives d'inclusions en C#: le mot clé using est utilisé pour référencer des espaces de noms.

using

Les variables locales en C# ne peuvent être utilisées sans avoir été initialisées au préalable.

5. Variables

Les destructeurs : en C#, il n'est pas possible de maîtriser l'appel de destructeurs, car ceux-ci sont appelés automatiquement par le ramasse-miettes (garbage collector).

Destructors

Les constructeurs: comparativement au C++, si vous ne fournissez pas un constructeur, un constructeur par défaut est automatiquement généré. Celui-ci se charge d'initialiser tous les champs avec leur valeur par défaut.

Instance Constructors
Default Values Table.

Le C# ne supporte pas les champs de type « bit ».

C++ Bit Fields

Les services d'entrée/sortie et de formatage de données s'appuient sur le RunTime du Framework .NET.

C# Language Tour
Formatting Numeric Results Table

En C#, les paramètres des méthodes ne peuvent avoir de valeur par défaut. Il faut surcharger les méthodes pour obtenir ce résultat.

Compiler Error CS0241

III - Les types de valeur et de référence

Le C# distingue les types de valeur (value) des types de référence (reference). Les types simples (int, long, double…) et les structures sont des types de valeur alors que les classes sont des types de référence tout comme les objets. Les types value représentent la donnée réelle stockée sur la pile et sont passés aux méthodes par valeur (une copie est réalisée). Les types reference contiennent l'adresse d'un objet stocké sur le tas et sont passés en paramètre par référence.

IV - Les structures

Les structures sont différentes en C#. En C++, les structures sont exactement comme une classe, sauf que l'héritage et l'accès par défaut est public et non privé. En C#, les structures sont conçues pour encapsuler de petits objets et sont de type value (donc passées par valeur). Elles sont limitées dans la mesure où elles ne peuvent dériver d'aucune classe sauf System.ValueType et qu'elles ne peuvent pas définir de constructeur par défaut (sans paramètres). En contrepartie l'utilisation d'une structure est préférable à celle d'une classe pour de très petits objets.

V - Tout dérive de la classe Object

En C# finalement tout dérive de la classe Object aussi bien les classes que vous créez que les types de valeur (int, struct…). La classe Object présente des méthodes utiles comme la méthode ToString. Prenons un exemple d'utilisation de la méthode ToString et de la méthode System.Console.WriteLine (équivalent du cout en C++). Considérons un objet myEmployee comme instance de l'objet Employee et un objet myCounter comme instance de l'objet Counter. Si nous écrivons le code suivant :

Console.WriteLine("The employee: {0}, the counter value: {1}", myEmployee, myCounter);

La méthode WriteLine va appeler la méthode virtuelle Objet.ToString de chacun de ces objets et substituer les chaînes que les paramètres vont retourner. Si la classe Employee ne surcharge pas la méthode ToString l'implémentation par défaut sera appelée laquelle retourne le nom de la classe. La classe Counter va surcharger la méthode ToString afin de retourner un entier ce qui produit à l'exécution le résultat suivant :

The employee: Employee, the counter value: 12

Que se passe-t-il si l'on transmet à la méthode WriteLine un entier ? Il n'est pas possible d'appeler la méthode ToString sur un entier, mais le compilateur va implicitement transformer l'entier en une instance de l'objet Object dont la valeur sera celle de l'entier. Cette transformation porte le terme de boxing.

Vous trouverez ci-joint l'exemple complet.

1. using System;

2. // Une classe qui ne surcharge pas la méthode ToString

5. public class Employee

6. {

7. }

8. // Une classe qui surcharge la méthode ToString

9. public class Counter

10. {

11. private int theVal;

12. public Counter(int theVal)

13. {

14. this.theVal = theVal;

15. }

16. public override string ToString()

17. {

18. Console.WriteLine("Calling Counter.ToString()"); return theVal.ToString();

19. }

20. }

23. public class Tester()

24. {

25. // À noter que la fonction Main contient une majuscule et que c'est une fonction membre de la classe

26. public static void Main()

27. {

28. // Création d'une instance de la classe

29. Tester t = new Tester();

30. t.Run();

31. }

32. // La méthode Run présente l'opération ToString et le boxing en C#

33. public void Run()

34. {

35. Employee myEmployee = new Employee();

36. Counter myCounter = new Counter(12);

37. Console.WriteLine("The employee: {0}, the counter value: {1}",myEmployee, myCounter);

38. // À noter que l'entier et les variables suivent une opération de boxing

39. int myInt = 5;

40. Console.WriteLine("Here are two integers: {0} and {1}", 17, myInt);

41. }

42. }

VI - La gestion des paramètres des prototypes de fonctions

En C# comme en C++ une méthode ne peut avoir qu'une seule valeur en retour. Pour pallier cette limitation en C++, les paramètres sont passés par références ou via des pointeurs. La méthode appelée change la valeur des paramètres et les rend accessibles à la méthode appelante. En C# quand vous passez une référence à une méthode vous avez accès à l'objet d'origine, comme en C++ avec le passage par référence ou les pointeurs. En revanche cela ne fonctionne pas avec les types de valeur. Si vous souhaitez passer un type de valeur par référence alors, il faut le faire précéder du mot clé ref.

public void GetStats(ref int age,ref int ID,ref int yearsServed)

Il est important de préciser que le mot réservé ref doit être utilisé aussi bien dans la déclaration de la méthode que dans la méthode appelante.

Fred.GetStats(ref age, ref ID,ref yearsServed);

Vous pouvez maintenant déclarer les champs age, ID, yearsServed dans la méthode appelante et les passer à la méthode GetStats et récupérer les valeurs modifiées en retour.

En C# il est nécessaire d'initialiser les variables avant de les passer à la méthode GetStats (definite assignment). Il est possible d'éviter cette initialisation en utilisant le mot clé out. En utilisant le mot clé out en C#, vous indiquez que la variable n'est pas initialisée et qu'elle est passée par référence.

Fred.GetStats(out age, out ID,out yearsServed);

Comme lors de l'utilisation du mot clé ref, le mot clé out doit être utilisé aussi bien dans la déclaration de la méthode que dans la méthode appelante.

Fred.GetStats(out age,out ID,out yearsServed);

VII - Le mot clé « new »

En C++, le mot clé New instancie un objet sur le tas. Le langage C# fait de même avec les types de reference. Pour les types de valeur comme les structures, l'objet est créé sur la pile et un constructeur est appelé.

Vous pouvez aussi créer une structure sur la pile sans utiliser le mot clé New, mais attention, car new initialise l'objet. Ce qui veut dire que toutes les valeurs de structure doivent être initialisées à la main (avant le passage à une méthode).

1. using System;

2. // Une structure simple avec 2 variables membres et un constructeur

3. public struct Point

4. {

5. public Point(int x, int y)

6. {

7. this.x = x;

8. this.y = y;

9. }

10. public int x;

11. public int y;

12. }

15. public class Tester

16. {

17. public static void Main()

18. {

19. Tester t = new Tester();

20. t.Run();

21. }

22. public void Run()

23. {

24. Point p1 = new Point (5, 12);

25. SomeMethod (p1);

26. Point p2;

27. // Création sans appeler la méthode New

29. // Ce code ne compilera pas, car les variables de p2 n'ont pas été initialisées

30. // SomeMethod(p2);

32. // Initialisation des variables manuellement

33. p2.x = 1;

34. p2.y = 2;

35. SomeMethod(p2);

36. }

37. private void SomeMethod(Point p)

38. {

39. Console.WriteLine("Point at {0} x {1}",p.x, p.y);

40. }

41. }

VIII - Les propriétés

En C++, les programmeurs essaient de garder les variables membres privées. C'est le principe même de l'encapsulation qui permet de modifier l'implémentation d'une classe sans en changer l'interface. Concrètement, le développeur C++ va créer des accesseurs permettant de modifier les valeurs de variables membres privées.

En C#, les propriétés sont les premiers membres d'une classe. Pour un client, une propriété ressemble à une variable membre, mais pour le développeur de la classe il s'agit plutôt d'une méthode. Les propriétés favorisent l'encapsulation et offrent au client un accès facilité aux membres de la classe.

Par exemple, considérons une classe Employee avec une propriété Age permettant à un client de valoriser ou d'extraire l'âge d'un employé.

1. public int Age

2. {

3. get

4. {

5. return age;

6. }

7. set

8. {

9. age = value;

10. }

11. }

Le mot clé value est défini implicitement au travers de la propriété. Si vous écrivez

Fred.Age = 17;

le compilateur va attribuer à value la valeur 17.

Il est possible d'implémenter une propriété en lecture seule, il suffit de ne pas implémenter d'accesseur set.

1. public int YearsServed

2. {

3. get

4. {

5. return yearsServed;

6. }

7. }

IX - Les tableaux

Le C# propose une classe de gestion des tableaux plus complète que le tableau traditionnel C/C++. Par exemple, il est impossible d'écrire en dehors des limites d'un tableau. De plus C# propose une classe ArrayList dont la taille peut grossir dynamiquement en fonction des besoins du programme.

Il existe trois types de tableaux : unidimensionnel, multidimensionnel et des tableaux de tableaux (jagged array).

Vous pouvez créer un tableau à une dimension de la manière suivante :

int[] myIntArray = new int[5];

ou l'initialiser ainsi :

int[] myIntArray = { 2, 4, 6, 8, 10 };

Vous pouvez créer un tableau à deux dimensions de la manière suivante :

int[,] myRectangularArray = new int[rows, columns];

ou l'initialiser ainsi :

int[,] myRectangularArray = { {0,1,2}, {3,4,5}, {6,7,8}, {9,10,11} };

Comme les jagged array sont des tableaux de tableaux il est nécessaire de fournir uniquement une seule dimension.

int[][] myJaggedArray = new int[4][];

et ensuite de créer chacun des tableaux internes :

myJaggedArray[0] = new int[5];
myJaggedArray[1] = new int[2];
myJaggedArray[2] = new int[3];
myJaggedArray[3] = new int[5];

Parce que les tableaux dérivent de l'objet System.Array, ils disposent de nombreuses méthodes parmi lesquelles Sort et Reverse.

X - Conclusion

À travers cet article nous avons abordé les principales différences entre le C# et le C++. Il en ressort que le passage au C# pour un développeur C++ expérimenté devrait se faire sans trop de difficultés la syntaxe se rapprochant beaucoup entre ces deux langages. Attention toutefois à ne pas tomber dans la facilité et à programmer en C# comme vous le feriez en C++ au risque d'avoir des comportements inattendus.

Remarque : il est tout à fait possible qu'il y ait des erreurs dans le document. Si vous en trouvez, ou bien souhaitez un peu plus d'explications sur certains points, veuillez m'envoyer un message privé via le forum afin de mettre à jour l'ensemble de ce document. D'avance merci.

XI - Note de la rédaction de Developpez.com

Nous tenons à remercier Winjerome pour la mise au gabarit, et Claude Leloup pour la relecture orthographique.