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. |
|
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#. |
|
Le type bool : il n'existe pas de conversion entre le type bool et d'autres types (particulièrement int). |
|
Le type long : en C#, le type de données long est sur 64 bits alors qu'il est sur 32 bits en C++. |
|
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. |
|
L'instruction switch : comparativement au C++, le C# n'autorise pas le passage automatique à chaque condition d'une clause switch. |
|
Le type delegate : les delegates ressemblent à des pointeurs de fonctions, mais ils sont sécurisés et fortement typés. |
|
Appel d'une fonction membre d'une classe surchargée à partir d'une classe dérivée. |
|
Utilisation du mot clé New afin de cacher explicitement une fonction héritée. |
|
La surcharge d'une méthode nécessite l'utilisation du mot clé override. |
|
Les directives de préprocessing sont utilisées pour la compilation conditionnelle. Il n'y a pas de fichier includes en C#. |
|
La gestion des exceptions : l'utilisation de la clause 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. |
|
L'utilisation du mot clé extern. |
|
L'utilisation du mot clé 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. |
|
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. |
|
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. |
|
Les pointeurs sont autorisés en C#, mais seulement dans un mode dit « unsafe ». |
|
La surcharge d'opérateurs est différente en C#. |
|
Les chaînes de caractères C# sont différentes de celles en C++. |
|
Le mot clé foreach permet de parcourir des tableaux et des collections. |
|
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). |
|
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. |
|
Les variables locales en C# ne peuvent être utilisées sans avoir été initialisées au préalable. |
|
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). |
|
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. |
|
Le C# ne supporte pas les champs de type « bit ». |
|
Les services d'entrée/sortie et de formatage de données s'appuient sur le RunTime du Framework .NET. |
|
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. |
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.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
using
System;
// Une classe qui ne surcharge pas la méthode ToString
public
class
Employee
{
}
// Une classe qui surcharge la méthode ToString
public
class
Counter
{
private
int
theVal;
public
Counter
(
int
theVal)
{
this
.
theVal =
theVal;
}
public
override
string
ToString
(
)
{
Console.
WriteLine
(
"Calling Counter.ToString()"
);
return
theVal.
ToString
(
);
}
}
public
class
Tester
(
)
{
// À noter que la fonction Main contient une majuscule et que c'est une fonction membre de la classe
public
static
void
Main
(
)
{
// Création d'une instance de la classe
Tester t =
new
Tester
(
);
t.
Run
(
);
}
// La méthode Run présente l'opération ToString et le boxing en C#
public
void
Run
(
)
{
Employee myEmployee =
new
Employee
(
);
Counter myCounter =
new
Counter
(
12
);
Console.
WriteLine
(
"The employee: {0}, the counter value: {1}"
,
myEmployee,
myCounter);
// À noter que l'entier et les variables suivent une opération de boxing
int
myInt =
5
;
Console.
WriteLine
(
"Here are two integers: {0} and {1}"
,
17
,
myInt);
}
}
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).
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
using
System;
// Une structure simple avec 2 variables membres et un constructeur
public
struct
Point
{
public
Point
(
int
x,
int
y)
{
this
.
x =
x;
this
.
y =
y;
}
public
int
x;
public
int
y;
}
public
class
Tester
{
public
static
void
Main
(
)
{
Tester t =
new
Tester
(
);
t.
Run
(
);
}
public
void
Run
(
)
{
Point p1 =
new
Point (
5
,
12
);
SomeMethod (
p1);
Point p2;
// Création sans appeler la méthode New
// Ce code ne compilera pas, car les variables de p2 n'ont pas été initialisées
// SomeMethod(p2);
// Initialisation des variables manuellement
p2.
x =
1
;
p2.
y =
2
;
SomeMethod
(
p2);
}
private
void
SomeMethod
(
Point p)
{
Console.
WriteLine
(
"Point at {0} x {1}"
,
p.
x,
p.
y);
}
}
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é.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
public
int
Age
{
get
{
return
age;
}
set
{
age =
value
;
}
}
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
.
2.
3.
4.
5.
6.
7.
public
int
YearsServed
{
get
{
return
yearsServed;
}
}
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.