Dans plusieurs chapitres concernant les bases du C#, nous avons vu la déclaration de variable, les branchements conditionnels et les boucles. Dans ce chapitre, nous verrons un dernier pilier fondamental de la programmation : les fonctions !
Les fonctions et leur utilité
Qu'est-ce qu'une fonction ?
Vous ne vous en doutez peut-être pas, mais si vous avez déjà un peu programmé ou suivi les cours sur C# de ce site, vous avez déjà utilisé une ou plusieurs fonctions. Nous avons par exemple utilisé une fonction lorsque nous avons affiché quelque chose dans la console :
Console.WriteLine("Hello, world !");
La fonction en question se nomme
WriteLine
et vous savez probablement déjà ce qu'elle fait : afficher une chaine de caractères dans la console. Vous avez donc déjà une bonne idée de ce qu'est une fonction : une chose que l'on peut appeler pour qu'elle fasse quelque chose.
L'utilité des fonctions
Remarquez que la définition que nous venons de donner au mot fonction n'est pas si différente de la définition même d'un programme. Une fonction peut donc être considérée comme un programme dans un programme. Un petit programme, donc, auquel les développeurs peuvent faire appel facilement pour construire une programme plus grand.
Se servir des fonctions revient donc à réutiliser des mini-programmes pour bâtir son propre programme. Ces mini-programmes peuvent avoir été codés par d'autres personnes, comme c'est le cas de la fonction
WriteLine
, mais également par vous même. Voyons ensemble comment utiliser des fonctions déjà existante en C# et comment en créer de nouvelles.
Utiliser des fonctions déjà existantes
Codez le moins possible !
Lorsque vous êtes programmeur, vous passez votre temps à utiliser des fonctions. Et devinez quoi ! La plupart du temps, les fonctions ne sont pas créées par vous ! Dans l'informatique réelle moderne, vous vous servirez très souvent de codes écrits par d'autres personnes. Tous les jours, des centaines de programmeurs développent et mettent à disposition des autres programmeurs des milliers de lignes de codes. Les fonctions sont justement la clé pour les réutiliser. C# (et son environnement .NET) fournissent déjà bon nombre de fonctions très utiles qui vous permettront déjà de faire de belles choses.
L'un des principes fondamentaux de la programmation est de ne pas se répéter et de ne pas réinventer la roue.
Se répéter signifie réécrire à plusieurs endroits un même code source source s'étendant sur plusieurs lignes. Réinventer la roue signifie recoder un programme que quelqu'un d'autre a déjà codé et mis à votre disposition. Les fonctions apportent une solution à ces deux problèmes.
Appeler des procédures
Nous connaissons déjà
Write
et
WriteLine
. Ces fonctions sont accessibles via le mot
Console
suivi d'un point
.
En C#, toutes les fonctions doivent être appelées par l'intermédiaire d'un mot ou plusieurs mots -séparé(s) par des points- qui est appelé leur domaine (scope en anglais). Dans le chapitre sur les boucles, nous avions par exemple vu la fonction
Sleep,
, à laquelle nous accédions comme ceci :
Threading.Thread.Sleep(1000);
Threading.Thread
est le domaine de la fonction
Sleep,
laquelle permet d'endormir le programme pendant un temps défini. De même,
Console
est le domaine des fonctions
Write
et
WriteLine
.
Toutes ces fonctions servent juste à faire quelque chose : on ne s'en sert pas pour récupérer un résultat. Notez que les fonctions qui ne renvoient pas de résultats sont souvent nommées procédures.
Utiliser des fonctions qui renvoient un résultat
Le programme Square
C# met à notre disposition une foule d'autres fonctions concernant d'autres domaines que la console. Nous pouvons par exemple utiliser des fonctions permettant d'effectuer des calculs. Le domaine
Math
contient par exemple pas mal de fonctions pour effectuer des opérations avancées. Cette librairie est particulièrement pratique quand on crée des jeux vidéos !
Créons un petit programme exploitant quelques fonctions et appelons-le Square.
Ce programme demandera à l'utilisateur d'entrer un nombre et de choisir une opération à effectuer sur ce nombre : élever au carré ou calculer la racine carrée.
Demander quelque chose à l'utilisateur
Commençons par demander un nombre à l'utilisateur :
using System;
namespace Square
{
class Program
{
static void Main(string[] args)
{
Console.Write("Entrez un nombre : ");
string reponseUtilisateur = Console.ReadLine();
}
}
}
Nous utilisons ici deux fonctions, la fonction
WriteLine
et la fonction
ReadLine
.
La première affiche quelque chose dans la console et la deuxième nous permet de récupérer ce qui a été saisi dans la console. La première fonction est donc une procédure tandis que la deuxième est une pure fonction.
Remarquez que nous utilisons ces fonctions différemment : WriteLine attend qu'on lui passe quelque chose en paramètre, c'est-à-dire qu'on écrive une valeur à l'intérieur de ses parenthèses. Ce n'est pas le cas de ReadLine. En y réfléchissant, c'est tout à fait logique puisque il faut bien que WriteLine sache ce qu'elle doit afficher tandis que ReadLine lira la réponse utilisateur au moment de l'exécution du programme.
Notez que
ReadLine
renvoie une chaine de caractères. Pour pouvoir exploiter ce qu'a entré l'utilisateur, je vais devoir transformer cette chaine en nombre (de type double, par exemple). Je vais pour cela utiliser une autre fonction, appelé
Parse
, laquelle est liée au domaine
Double
Cette fonction prend en paramètre une chaine de caractère et renvoie un nombre sous la forme d'un double.
// ...
double nombre = Double.Parse(reponseUtilisateur);
// ...
Ensuite, nous demandons à l'utilisateur d'entrer un calcul à effectuer :
Console.Write(
"Quel calcul souhaitez vous effectuer : \n"
+ " 1. Élever au carré\n"
+ " 2. Calculer la racine carrée\n"
+ "Entrez votre choix : "
);
string souhaitUtilisateur = Console.ReadLine();
Effectuer une opération mathématique
En fonction de la réponse de l'utilisateur, on utilisera une fonction mathématique différente. Si la réponse est 1, nous écrirons le carré et si c'est 2, nous écrirons la racine carré. Pour calculer ces éléments, nous utiliserons les fonctions
Math.Sqrt
-racine carrée- et
Math.Pow
-fonction puissance- :
if (souhaitUtilisateur == "1")
{
resultat = Math.Pow(nombre, 2);
}
else if (souhaitUtilisateur == "2")
{
resultat = Math.Sqrt(nombre);
}
else
{
Console.WriteLine("le choix entré n'est pas valide");
}
La fonction Sqrt renvoie la racine carrée du nombre passée en paramètre.
La fonction Pow prend, elle, deux paramètres, le premier est le nombre à élever, le deuxième est la puissance à laquelle on souhaite l'élever. Nous souhaitons élever nombre au carré, nous utiliserons donc
Math.Pow(nombre,2)
!
Il ne nous reste plus qu'à afficher le résultat :
// ...
Console.WriteLine("Le choix résultat est " + resultat);
// ...
Nous obtenons le code suivant :
using System;
namespace Square
{
class Program
{
static void Main(string[] args)
{
Console.Write("Entrez un nombre : ");
string reponseUtilisateur = Console.ReadLine();
double nombre = Double.Parse(reponseUtilisateur);
Console.Write(
"Quel calcul souhaitez vous effectuer : \n"
+ " 1. Élever au carré\n"
+ " 2. Calculer la racine carrée\n"
+ "Entrez votre choix : "
);
string souhaitUtilisateur = Console.ReadLine();
double resultat = 0;
if (souhaitUtilisateur == "1")
{
resultat = Math.Pow(nombre, 2);
}
else if (souhaitUtilisateur == "2")
{
resultat = Math.Sqrt(nombre);
}
else
{
Console.WriteLine("le choix entré n'est pas valide");
}
Console.WriteLine("Le choix résultat est " + resultat);
}
}
}
Notre programme se comporte bien comme attendu :
Le truc en plus : utiliser l'auto-complétion
Si vous utilisez un éditeur de code un peu moderne, il est probable que celui-ci vous donne la possibilité d'utiliser l'auto-complétion. Par exemple, sous Visual Studio Code, vous avez la possibilité de taper Ctrl+Espace à la suite d'un domaine pour connaitre l'ensemble des fonctions disponibles dans le domaine. Par exemple, pour connaitre toutes les fonctions disponibles dans le domaine Math, j'écrirai Math. suivi de <Ctrl+Espace> et j'obtiendrai ceci :
Les fonctions, précédées d'un icône en forme de cube, s'affichent et peuvent être sélectionnées de façon à faire apparaître leurs informations à droite (paramètres à passer et éventuellement descriptif écrit). Cette fonctionnalité est extrêmement utile pour prendre en main du code qui n'est pas à nous !
Bien, nous savons maintenant comment utiliser des fonctions. Dans la dernière partie de ce cours, vous allez apprendre comment créer vos propres fonctions. Let's go !
Créer des fonctions
Nous avons vu comment utiliser des fonctions codées par d'autres. Voyons maintenant comment créer nos propres fonctions.
Une méthode dans un domaine
On l'a vu : les fonctions en C# sont toujours liées à un domaine. Ainsi, en C#, on parle plus volontiers de méthodes, que de fonctions. Ce domaine peut être une classe ou un objet. Nous verrons plus tard ce que sont les objets mais pour l'instant, concentrons-nous sur les méthodes liées à une classe car leur comportement est plus simple à appréhender quand on débute. Les méthodes liées à une classe sont dites statiques. Dans la fin de ce chapitre, les fonctions que nous créerons seront donc en réalité des méthodes statiques.
Notre première fonction
Créons un mini-projet appelé Hello Fonction dans lequel nous créerons nos premières fonctions. La première fonction que nous créerons sera très simple et affichera simplement "bonjour". Notre programme ressemble pour l'instant à ceci :
using System;
namespace Hello_Fonction
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
}
Jusqu'ici, nous avions ajouté du code uniquement à l'intérieur des deux accolades qui entourent le
Console.WriteLine
. Dans cette partie, nous allons enfin nous extraire de ces accolades et remonter d'un cran dans la hiérarchie ! Nous ajouterons du code à l'intérieur de la classe Programme, laquelle est annoncée à l'aide du mot-clé class. À l'intérieur de cette classe, nous créerons notre méthode statique, laquelle affichera simplement bonjour. Voici à quoi elle ressemble :
static void SayHello()
{
}
Pour dire à notre ordinateur que la méthode est statique, je dois mettre le mot-clé static devant la déclaration de ma fonction. Ensuite je dois renseigner le type de retour de ma fonction. Nous verrons tout à l'heure ce que c'est. Pour l'instant, nous mettons void. Ensuite, en troisième position, nous donnons à notre fonction un nom : elle s'appellera SayHello. Enfin, nous mettons un couple de parenthèses après le nom de la fonction. ce couple de parenthèses contiendra plus tard la liste des paramètres de la fonction. Nous allons aussi voir cela un peu plus loin.
Après la déclaration de ma fonction j'ouvre des accolades. Ces accolades contiennent le code qui sera exécuté lorsque je ferai appel à ma fonction. Pour l'instant, les accolades ne contiennent aucun code : ma fonction ne fait donc rien, à l'heure actuelle. Notez que le nom d'une méthode doit, par convention, commencer par une majuscule. Si ma fonction compte plusieurs mots, la première lettre de chaque doit également commencer par une majuscule.
Ajoutons du le code permettant de dire bonjour à l'utilisateur dans la méthode SayHello.
static void SayHello()
{
Console.WriteLine("Bonjour, Utilisateur !");
}
Notre méthode doit bien dire : "Bonjour, Utilisateur !" Exécutons, notre programme pour le vérifier :
Oups, notre programme n'exécute pas le code de notre fonction mais celui qui est juste au dessus. C'est tout à fait normal. Pour que le code d'une fonction soit exécuté, il faut l'appeler dans le code principal qui se trouve être celui présent dans le Main, et dans lequel nous avions l'habitude de coder jusqu'à présent.
Appeler notre fonction
Appel simple
Pour appeler cette fonction, il suffit d'écrire son nom suivi d'un couple de parenthèse :
SayHello()
Cette simplicité d'écriture est dûe au fait qu'on appelle cet fonction dans le même domaine que celui où elle est déclarée. Nous avons vu que dans le cas où nous appelons une fonction depuis un autre domaine, ce nom de domaine est à préciser avant l'appel à la fonction.
L'appel à une fonction est une instruction. Pour que la syntaxe soit correcte, je dois donc mettre un point virgule à la suite de l'appel à ma fonction. remplaçons notre "Hello World" par un appel à notre fonction. Cela nous donne :
static void Main(string[] args)
{
SayHello();
}
static void SayHello()
{
Console.WriteLine("Bonjour, Utilisateur !");
}
Lorsque on exécute ce code, cela fonctionne !
Ajouter un paramètre à notre fonction
Notre fonction s'exécute bien ! Néanmoins, elle n'est pas très intéressante pour l'instant. Améliorons-la en lui donnant la possibilité de dire bonjour à n'importe quel utilisateur. Pour cela, notre fonction aura besoin de savoir à quel utilisateur elle doit dire bonjour. Pour lui transmettre cette information lorsque nous l'appelons, nous passerons ce que l'on appelle un paramètre ou encore un argument à notre fonction. Cette argument sera passé et récupéré à l'intérieur des parenthèses. Du côté de la déclaration de notre fonction, nous aurons ceci :
static void SayHello(string user)
{
Je dis à mon ordinateur que ma fonction prend un argument que j'appelle user et qui doit être de type string. À partir du moment où je fais cela, mon éditeur m'informe d'une erreur de syntaxe dans l'appel de ma fonction.
C'est normal, maintenant que ma fonction prend un paramètre, je dois fournir une valeur pour ce paramètre lors de mon appel. Appelons, notre utilisateur Gérard :
static void Main(string[] args)
{
SayHello("Gérard");
}
Notre fonction attend une chaine de caractère, Gérard est donc un string. Nous aimerions pouvoir dire bonjour à Josianne, ensuite :
static void Main(string[] args)
{
SayHello("Gérard");
SayHello("Josianne");
}
Testons :
Exploiter le(s) paramètre(s) d'une fonction
Zut, pour l'instant, notre paramètre n'est pas pris en compte. Normal, puisque nous ne l'utilisons pas dans notre fonction. Réglons cela :
static void SayHello(string user)
{
Console.WriteLine("Bonjour, " + user + " !");
}
Ici, nous remplaçons "Utilisateur" par la valeur passée en paramètre. Ainsi, notre fonction se comporte comme attendu !
Nous commençons à percevoir un peu l'intérêt, n'est-ce-pas ? La même fonction (ou méthode statique) est appelée deux fois avec simplement des arguments différents et peut ainsi s'adapter à ce qu'on attend d'elle.
Cette méthode est un exemple de ce qu'on appelle une procédure : une fonction qui se contente de faire quelque chose mais qui ne retourne aucun résultat. Voyons maintenant les fonctions qui ne sont pas des procédures.
Retourner une valeur dans une fonction
La fonction factorielle
Dans cette partie, nous allons coder une fonction appelée Factorielle. Cette fonction est une fonction mathématique permettant de calculer la chose suivante :
- Si l'argument qui lui est passé est inférieur à 1, alors elle renvoie la valeur 1.
- Si l'argument est supérieur à 1, alors elle renvoie le produit du nombre et de tous les nombres qui le précède jusqu'à 1. Ainsi la factorielle de 4 est 24 car 24 = 4 * 3 * 2 * 1
Première ébauche
Créons un projet nommé Factorielle. Dans ce projet, nous allons d'abord écrire notre code à l'endroit habituel. Ensuite, nous rendrons notre code réutilisable en le mettant dans une fonction. Commençons par demander un nombre à l'utilisateur.
static void Main(string[] args)
{
Console.WriteLine("Entrez un nombre : ");
string reponseUtilisateur = Console.ReadLine();
int nombre = Int32.Parse(reponseUtilisateur);
}
Le calcul de la factorielle peut se faire en utilisant une boucle for, comme ceci :
int fac = 1;
for(int i = 1; i <= nombre; i++){
fac = fac * i;
}
fac contient à la fin de la boucle la factorielle du nombre saisi par l'utilisateur. Il ne nous reste qu'à afficher le résultat.
Console.WriteLine("La factorielle du nombre est : " + fac);
Amélioration
Supposez maintenant que notre programme ait à nouveau besoin de calculer une factorielle. Par exemple en redemandant à l'utilisateur un nombre et en effectuant à nouveau le calcul. Une idée serait de copier coller le code de cette façon :
static void Main(string[] args)
{
Console.WriteLine("Entrez un nombre : ");
string reponseUtilisateur = Console.ReadLine();
int nombre = Int32.Parse(reponseUtilisateur);
int fac = 1;
for (int i = 1; i <= nombre; i++)
{
fac = fac * i;
}
Console.WriteLine("La factorielle du nombre est : " + fac);
Console.WriteLine("Entrez un autre nombre : ");
string reponseUtilisateur2 = Console.ReadLine();
int nombre2 = Int32.Parse(reponseUtilisateur2);
int fac2 = 1;
for (int i = 1; i <= nombre2; i++)
{
fac2 = fac2 * i;
}
Console.WriteLine("La factorielle du nombre est : " + fac2);
}
fac
devient
fac2
,
reponseUtilisateur
devient
reponseUtilisateur2
et tout va bien : notre code fonctionne. Cependant, notre code a perdu en qualité. Pour deux raisons principales :
- Notre code commence à devenir long et difficile à relire. Pas évident de comprendre ce qu'il fait au premier coup d’œil.
- Notre code est plus difficile à maintenir : en effet, nous avons un code qui fait exactement la même chose à deux endroits différents. Si je me rends compte d'une erreur dans mon code, je serais obligé de corriger le problème à deux endroits. Imaginez l'ampleur du problème si on recopie le même code à 50 endroits différents sur un programme de 100 000 lignes ! Ingérable.
Pour améliorer la qualité de notre code, nous allons créer une fonction permettant de calculer et de récupérer la factorielle d'un nombre. Nous appellerons cette fonction fac. Voyez plutôt :
static void Fac(int nombre)
{
int fac = 1;
for (int i = 1; i <= nombre; i++)
{
fac = fac * i;
}
Console.WriteLine("La factorielle du nombre est : " + fac);
}
En utilisant cette fonction, je peux améliorer sensiblement mon code principal !
static void Main(string[] args)
{
Console.WriteLine("Entrez un nombre : ");
string reponseUtilisateur = Console.ReadLine();
int nombre = Int32.Parse(reponseUtilisateur);
Fac(nombre);
Console.WriteLine("Entrez un autre nombre : ");
string reponseUtilisateur2 = Console.ReadLine();
int nombre2 = Int32.Parse(reponseUtilisateur2);
Fac(nombre2);
}
Deuxième amélioration
Retourner un résultat
Cependant, en l'état, notre fonction n'est pas encore très satisfaisante car elle n'est pas très souple : elle ne peut que afficher "La factorielle du nombre est : " suivi de la valeur calculée. Pour être vraiment réutilisable dans une autre contexte, l'idéal serait de pouvoir récupérer la valeur calculée par la fonction. Pour cela, nous allons utiliser le mot clé return, lequel permet d'interrompre la fonction et de renvoyer un résultat à l'extérieur de celle-ci.
static void Fac(int nombre)
{
int fac = 1;
for (int i = 1; i <= nombre; i++)
{
fac = fac * i;
}
return fac; // ma fonction s'interrompt ici
Console.WriteLine("La factorielle du nombre est : " + fac);
}
La fonction s'interrompt au niveau du return et renvoie le résultat à l'extérieur. La ligne suivante sera donc inutile car elle ne sera jamais exécutée. Nous pouvons l'enlever.
static void Fac(int nombre)
{
int fac = 1;
for (int i = 1; i <= nombre; i++)
{
fac = fac * i;
}
return fac; // ma fonction s'interrompt ici
}
À ce moment-là notre éditeur de code se réveille et nous annonce une erreur : Notre fonction revoie une valeur (qui est un entier) et annonce renvoyer un void, c'est-à-dire rien du tout. Il faut que j'annonce à mon ordinateur que ma fonction renvoie un entier. Pour cela, je dois modifier la façon dont je déclare ma fonction en remplaçant void par int.
static int Fac(int nombre)
Récupérer un résultat
Maintenant que ma fonction renvoie un résultat, je peux le récupérer dans mon code principal. Dans une variable par exemple :
static void Main(string[] args)
{
//...
int fac1 = Fac(nombre);
//...
int fac2 = Fac(nombre2);
}
Je peux ensuite afficher ce résultat... ou bien faire tout autre chose avec !
static void Main(string[] args)
{
// ...
int fac1 = Fac(nombre);
Console.WriteLine("La factorielle du nombre " + nombre + " est " + fac1 + "!");
// ...
int fac2 = Fac(nombre2);
Console.WriteLine("La factorielle du nombre " + nombre2 + " est " + fac2 + "!");
}
Encore plus de fonction
Il est possible d'améliorer encore ce code ! En effet, ce dernier possède encore beaucoup de répétitions. Voici une version plus claire, plus lisible, et bien plus qualitative !
using System;
namespace Factorielle
{
class Program
{
static void Main(string[] args)
{
int nombre = DemanderNombre("Entrez un nombre");
int fac1 = Fac(nombre);
Console.WriteLine("La factorielle du nombre " + nombre + " est " + fac1 + "!");
int nombre2 = DemanderNombre("Entrez un autre nombre");
int fac2 = Fac(nombre2);
Console.WriteLine("La factorielle du nombre " + nombre2 + " est " + fac2 + "!");
}
static int DemanderNombre(String phrase)
{
Console.WriteLine(phrase);
string reponseUtilisateur = Console.ReadLine();
int nombre = Int32.Parse(reponseUtilisateur);
return nombre;
}
static int Fac(int nombre)
{
int fac = 1;
for (int i = 1; i <= nombre; i++)
{
fac = fac * i;
}
return fac;
}
}
}
Prenez le temps d'analyser ce code pour bien comprendre son fonctionnement.
Bien ! Nous en avons fini avec les fonctions et avec toutes les notions de bases. C'était long et parfois un peu fastidieux mais une fois que vous avez compris tout cela, vous êtes armés pour commencer à créer.
Si vous souhaitez approfondir le C# (ce que je vous recommande vivement), d'autres cours comme celui-ci suivront, lesquels porteront sur des notions plus avancées du langage telles que l'orienté objet. Restez connectés !