Aller au menu - Aller au contenu

Icône Mode de passage des paramètres à une méthode

Mise à jour : 02/02/2012
Difficulté : Intermédiaire Intermédiaire Creative Commons BY-NC-SA
22 955 visites depuis 7 jours, dont 200 sur ce chapitre classé 15/786
Dans les chapitres précédents, nous avons décrit comment on pouvait passer des paramètres à une méthode. Nous avons également vu que les types du framework .NET pouvaient être des types valeur ou des types référence. Ceci influence la façon dont sont traités les paramètres d'une méthode. Nous allons ici préciser un peu ce fonctionnement.

Dans ce chapitre, je vais illustrer mes propos en utilisant des méthodes statiques écrites dans la classe Program, générée par Visual C# Express. Le but est de simplifier l'écriture et de ne pas s'encombrer d'objets inutiles. Évidemment, tout ce que nous allons voir fonctionne également de la même façon avec les méthodes non statiques présentes dans des objets.
Sommaire du chapitre :
Icône du chapitre
Chapitre précédent Sommaire Chapitre suivant

Passage de paramètres par valeur

Tout au début du tutoriel, nous avons appelé des méthodes en passant des types simples (int, string, etc.). Nous avons vu qu’il s’agissait de types valeur qui possèdent directement la valeur dans la variable.

Lorsque nous passons des types valeur en paramètre d’une méthode, nous utilisons un passage de paramètre par valeur.

Décortiquons l’exemple suivant :

Code : C#
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
static void Main(string[] args)
{
    int age = 30;
    Doubler(age);
    Console.WriteLine(age);
}

public static void Doubler(int valeur)
{
    valeur = valeur * 2;
    Console.WriteLine(valeur);
}


Nous déclarons dans la méthode Main() une variable age, de type entier, à laquelle nous affectons la valeur 30.
Nous appelons ensuite la méthode Doubler() en lui passant cette variable en paramètre.

Ce qu’il se passe c’est que le compilateur fait une copie de la valeur de la variable age pour la mettre dans la variable valeur de la méthode Doubler(). La variable valeur a une portée égale au corps de la méthode Doubler().

Nous modifions ensuite la valeur de la variable valeur en la multipliant par deux. Étant donné que la variable valeur a reçu une copie de la variable age, c'est-à-dire que le compilateur a dupliqué la valeur 30, le fait de modifier la variable valeur ne change en rien la valeur de la variable age.

En effet, si nous exécutons le code du dessus, nous allons avoir :

Image utilisateur


Nous passons 30 à la méthode Doubler() qui calcule le double de la variable valeur. On affiche 60. Lorsqu’on revient dans la méthode Main(), nous retrouvons la valeur initiale de la variable age. Elle n’a bien sûr pas été modifiée car la méthode Doubler() a travaillé sur une copie.

Rappelez-vous que ceci est possible car les types intégrés sont facilement copiables, car peu évolués.

Passage de paramètres en mise à jour

Oui mais si je veux modifier la valeur ?


Pour pouvoir modifier la valeur du paramètre passé, il faut indiquer que la variable est en mode « mise à jour ». Cela se fait grâce au mot-clé « ref » que nous pourrons utiliser ainsi :

Code : C#
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
static void Main(string[] args)
{
    int age = 30;
    Doubler(ref age);
    Console.WriteLine(age);
}

public static void Doubler(ref int valeur)
{
    valeur = valeur * 2;
}


Comme on peut s’en douter, ce code affiche 60.

Le mot-clé ref s’utilise avant la définition du paramètre dans la méthode. Cela implique qu’il soit également utilisé au moment d’appeler la méthode.

Vous aurez remarqué que le mot-clé utilisé est ref et qu'il ressemble beaucoup au mot « référence ». Ce n’est évidemment pas un hasard.
En fait, avec ce mot-clé nous demandons au compilateur de passer en paramètre une référence vers la variable age plutôt que d’en faire une copie. Ainsi, la méthode Doubler() récupère une référence et la variable valeur référence alors la même valeur que la variable age. Ceci implique que toute modification de la valeur référencée provoque un changement sur la variable source puisque les variables référencent la même valeur.

Voilà pourquoi la variable est modifiée après le passage dans la méthode Doubler().
Bien sûr, il aurait tout à fait été possible de faire en sorte que la méthode Doubler() renvoie un entier contenant la valeur passée en paramètre multipliée par 2 :

Code : C#
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
static void Main(string[] args)
{
    int age = 30;
    age = Doubler(age);
    Console.WriteLine(age);
}

public static int Doubler(int valeur)
{
    return valeur * 2;
}


C’est ce que nous avons toujours fait auparavant. Voici donc une autre façon de faire qui peut être bien utile quand il y a plus d’une valeur à renvoyer.

Passage des objets par référence

C’est aussi comme ça que cela fonctionne pour les objets. Nous avons vu que les variables qui stockent des objets possèdent en fait la référence de l’objet. Le fait de passer un objet à une méthode équivaut à passer la référence de l’objet en paramètres. Ainsi, c’est comme si on utilisait le mot-clé ref implicitement.

Le code suivant :

Code : C#
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
static void Main(string[] args)
{
    Voiture voiture = new Voiture { Couleur = "Grise" };
    Repeindre(voiture);
    Console.WriteLine(voiture.Couleur);
}

public static void Repeindre(Voiture voiture)
{
    voiture.Couleur = "Bleue";
}


va donc créer un objet Voiture et le passer en paramètres à la méthode Repeindre(). Comme Voiture est un type référence, il n’y a pas de duplication de l’objet et une référence est passée à la méthode.
Lorsque nous modifions la propriété Couleur de la voiture, nous modifions bien le même objet que celui présent dans la méthode Main().

Nous aurons donc :

Image utilisateur


Il est à noter quand même que la variable voiture de la méthode Repeindre est une copie de la variable voiture de la méthode Main() qui contiennent toutes les deux une référence vers l'objet de type Voiture. Cela veut dire que l'on accède bien au même objet, d'où le résultat, mais que les deux variables sont indépendantes.
Si nous modifions directement la variable, avec par exemple :

Code : C#
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
static void Main(string[] args)
{
    Voiture voiture = new Voiture { Couleur = "Grise" };
    Repeindre(voiture);
    Console.WriteLine(voiture.Couleur);
}

public static void Repeindre(Voiture voiture)
{
    voiture.Couleur = "Bleue";
    voiture = null;
}


Alors, ce code continuera à fonctionner, car la variable voiture de la méthode Main() ne vaut pas null. Le fait de modifier la variable de la méthode Repeindre, qui est une copie, n'affecte en rien la variable de la méthode Main().

Par contre, elle est affectée si nous utilisons le mot-clé ref. Par exemple le code suivant :

Code : C#
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
static void Main(string[] args)
{
    Voiture voiture = new Voiture { Couleur = "Grise" };
    Repeindre(ref voiture);
    Console.WriteLine(voiture.Couleur);
}

public static void Repeindre(ref Voiture voiture)
{
    voiture.Couleur = "Bleue";
    voiture = null;
}


provoquera une erreur. En effet, cette fois-ci, c'est bien la référence qui est passée à nulle et pas une copie de la variable contenant la référence...

Une subtile différence. :-°

Passage de paramètres en sortie

Enfin, le dernier mode de passage est le passage de paramètres en sortie. Il permet de faire en sorte qu’une méthode force l’initialisation d’une variable et que l’appelant récupère la valeur initialisée.
Nous avons déjà vu ce mode de passage quand nous avons étudié les conversions. Souvenez-vous de ce code :

Code : C#
1
2
3
4
5
6
string chaine = "1234";
int nombre;
if (int.TryParse(chaine, out nombre))
    Console.WriteLine(nombre);
else
    Console.WriteLine("Conversion impossible");


La méthode TryParse permet de tester la conversion d’une chaîne. Elle renvoie vrai ou faux en fonction du résultat de la conversion et met à jour l’entier qui est passé en paramètre en utilisant le mot-clé « out ».
Si la conversion réussit, alors l’entier nombre est initialisé avec la valeur de la conversion, calculée dans la méthode TryParse.
Nous pouvons utiliser ensuite la variable nombre car le mot-clé out garantit que la variable sera initialisée dans la méthode.

En effet, si nous prenons l’exemple suivant :

Code : C#
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
static void Main(string[] args)
{
    int age = 30;
    int ageDouble;
    Doubler(age, out ageDouble);
}

public static void Doubler(int age, out int resultat)
{
}


Nous aurons une erreur de compilation :

Code : Console
Le paramètre de sortie 'resultat' doit être assigné avant que le contrôle quitte la méthode actuelle


En effet, il faut absolument que la variable resultat qui est marquée en sortie ait une valeur avant de pouvoir sortir de la méthode.
Nous pourrons corriger cet exemple avec :

Code : C#
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
static void Main(string[] args)
{
    int age = 30;
    int ageDouble;
    Doubler(age, out ageDouble);
}

public static void Doubler(int age, out int resultat)
{
    resultat = age * 2;
}


Après l’appel de la méthode Doubler(), ageDouble vaudra donc 60.

Oui, mais quel intérêt par rapport à un return normal ?

Ici, aucun. Cela est pertinent quand nous souhaitons renvoyer plusieurs valeurs, comme c’est le cas pour la méthode TryParse qui renvoie le résultat de la conversion et si la conversion s’est bien passée.
Bien sûr, si l’on n’aime pas trop le mot-clé out, il est toujours possible de créer un objet contenant deux valeurs que l’on retournera à l’appelant, par exemple :

Code : C#
 1
 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
public class Program
{
    static void Main(string[] args)
    {
        string nombre = "1234";
        Resultat resultat = TryParse(nombre);
        if (resultat.ConversionOk)
            Console.WriteLine(resultat.Valeur);
    }

    public static Resultat TryParse(string chaine)
    {
        Resultat resultat = new Resultat();
        int valeur;
        resultat.ConversionOk = int.TryParse(chaine, out valeur);
        resultat.Valeur = valeur;
        return resultat;
    }

}

public class Resultat
{
    public bool ConversionOk { get; set; }
    public int Valeur { get; set; }
}


Ici, notre méthode TryParse renvoie un objet Resultat qui contient les deux valeurs résultantes de la conversion.

Q.C.M.

Que vaut vitesse à la fin du code suivant ?

Code : C#
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
static void Main(string[] args)
{
    int vitesse;

    vitesse = ModifierVitesse(out vitesse);
}

public int ModifierVitesse(out int vitesse)
{
    return 10;
}
Que vaut la variable age à la fin du code suivant ?

Code : C#
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
static void Main(string[] args)
{
    int age = 10;

    Modifier(ref age);

    Console.WriteLine(age);
}

public static void Modifier(ref int age)
{
    MettreAJour(out age);
    age *= 2;
    MettreAuCube(age);
}

public static void MettreAJour(out int age)
{
    age = 20;
}

public static void MettreAuCube(int age)
{
    age = (int)Math.Pow(age, 3);
}

Statistiques de réponses au QCM

En résumé


  • Le type d'une variable passée en paramètres d'une méthode influence la façon dont elle est traitée.
  • Un passage par valeur effectue une copie de la valeur de la variable et la met dans la variable de la méthode.
  • Un passage par référence effectue une copie de la référence mais continue de pointer sur le même objet.
  • On utilise le mot-clé ref pour passer une variable de type valeur à une méthode afin de la modifier.
Chapitre précédent Sommaire Chapitre suivant

Partager

2 commentaires pour "Mode de passage des paramètres à une méthode"
Note moyenne : 3.05 / 4 (230 votes)
Pseudo Commentaire
Hors ligne Darioo2 # Posté le 30/03/2012 à 20:03:27
Avatar

Avis : Bon

En fait en utilisant ref, on indique l'espace memoire ou est contenue la variable plutot que de faire une copie !
L'utilisation de ref c'est comme utiliser les pointeurs en C/C++ ?
Hors ligne nico.pyright # Posté le 01/04/2012 à 18:23:16
Groupe : Auteurs

oui, à peu près
 

Voir tous les commentaires