Retours ref et variables locales refRef returns and ref locals

À compter de C# 7.0, C# prend en charge les valeurs de retour de référence (retours ref).Starting with C# 7.0, C# supports reference return values (ref returns). Une valeur de retour de référence permet à une méthode de retourner à un appelant une référence à une variable, plutôt qu’à une valeur.A reference return value allows a method to return a reference to a variable, rather than a value, back to a caller. L’appelant peut alors choisir de traiter la variable retournée comme si elle était retournée par valeur ou par référence.The caller can then choose to treat the returned variable as if it were returned by value or by reference. L’appelant peut créer une nouvelle variable qui est elle-même une référence à la valeur retournée, appelée variable locale ref.The caller can create a new variable that is itself a reference to the returned value, called a ref local.

Qu’est-ce qu’une valeur de retour de référence ?What is a reference return value?

La plupart des développeurs sont familiarisés avec le passage d’un argument à une méthode appelée par référence.Most developers are familiar with passing an argument to a called method by reference. La liste d’arguments d’une méthode appelée inclut une variable passée par référence.A called method's argument list includes a variable passed by reference. Chaque modification de sa valeur par la méthode appelée est respectée par l’appelant.Any changes made to its value by the called method are observed by the caller. Une valeur de retour de référence signifie qu’une méthode retourne une référence (ou un alias) à une variable.A reference return value means that a method returns a reference (or an alias) to some variable. L’étendue de cette variable doit inclure la méthode.That variable's scope must include the method. La durée de vie de cette variable doit s’étendre au-delà du retour de la méthode.That variable's lifetime must extend beyond the return of the method. Des modifications apportées par l’appelant à la valeur de retour de la méthode portent sur la variable qui est retournée par la méthode.Modifications to the method's return value by the caller are made to the variable that is returned by the method.

La déclaration qu’une méthode retourne une valeur de retour de référence indique que la méthode retourne un alias vers une variable.Declaring that a method returns a reference return value indicates that the method returns an alias to a variable. L’intention de conception est souvent que le code appelant ait accès à cette variable à travers l’alias, et qu’il puisse également la modifier.The design intent is often that the calling code should have access to that variable through the alias, including to modify it. C’est pourquoi les méthodes de retour par référence ne peuvent pas avoir le type de retour void.It follows that methods returning by reference can't have the return type void.

Certaines restrictions s’appliquent à l’expression qu’une méthode peut retourner comme valeur de retour de référence.There are some restrictions on the expression that a method can return as a reference return value. Les restrictions sont les suivantes :Restrictions include:

  • La valeur de retour doit avoir une durée de vie qui s’étend au-delà de l’exécution de la méthode.The return value must have a lifetime that extends beyond the execution of the method. En d’autres termes, il ne peut pas s’agir d’une variable locale dans la méthode qui la retourne.In other words, it cannot be a local variable in the method that returns it. Il peut s’agir d’un champ d’instance ou statique d’une classe, ou bien un argument passé à la méthode.It can be an instance or static field of a class, or it can be an argument passed to the method. Une tentative de retour d’une variable locale génère l’erreur du compilateur CS8168, « Impossible de retourner la variable locale 'obj' par référence, car il ne s’agit pas d’une variable locale de référence ».Attempting to return a local variable generates compiler error CS8168, "Cannot return local 'obj' by reference because it is not a ref local."

  • La valeur de retour ne peut pas être le littéral null.The return value cannot be the literal null. Le retour de null génère l’erreur de compilateur CS8156, « Impossible d’utiliser une expression dans ce contexte, car elle ne peut pas être retournée par référence ».Returning null generates compiler error CS8156, "An expression cannot be used in this context because it may not be returned by reference."

    Une méthode avec un retour de référence peut retourner un alias vers une variable dont la valeur est actuellement la valeur (non instanciée) null ou un type nullable pour un type valeur.A method with a ref return can return an alias to a variable whose value is currently the null (uninstantiated) value or a nullable type for a value type.

  • La valeur de retour ne peut pas être une constante, un membre d’énumération, la valeur de retour par valeur d’une propriété, ou une méthode d’une class ou d’un struct.The return value cannot be a constant, an enumeration member, the by-value return value from a property, or a method of a class or struct. Le non-respect de cette règle génère l’erreur de compilateur CS8156, « Impossible d’utiliser une expression dans ce contexte, car elle ne peut pas être retournée par référence ».Violating this rule generates compiler error CS8156, "An expression cannot be used in this context because it may not be returned by reference."

En outre, les valeurs de retour de référence ne sont pas autorisées sur les méthodes asynchrones.In addition, reference return values are not allowed on async methods. Une méthode asynchrone peut être retournée avant la fin de son exécution, même si sa valeur de retour est encore inconnue.An asynchronous method may return before it has finished execution, while its return value is still unknown.

Définition d’une valeur de retour de référenceDefining a ref return value

Une méthode qui retourne une valeur de retour de référence doit remplir les deux conditions suivantes :A method that returns a reference return value must satisfy the following two conditions:

  • Dans la signature de méthode, le mot clé ref précède le type de retour.The method signature includes the ref keyword in front of the return type.
  • Pour chaque instruction return dans le corps de la méthode, le mot clé ref précède le nom de l’instance retournée.Each return statement in the method body includes the ref keyword in front of the name of the returned instance.

L’exemple suivant montre une méthode qui remplit ces conditions et retourne une référence à un objet Person nommé p :The following example shows a method that satisfies those conditions and returns a reference to a Person object named p:

public ref Person GetContactInformation(string fname, string lname)
{
    // ...method implementation...
    return ref p;
}

Utilisation d’une valeur de retour de référenceConsuming a ref return value

La valeur de retour de référence est l’alias vers une autre variable dans l’étendue de la méthode appelée.The ref return value is an alias to another variable in the called method's scope. Toute utilisation d’une valeur de retour de référence revient à utiliser la variable dont elle est l’alias :You can interpret any use of the ref return as using the variable it aliases:

  • Quand vous lui affectez une valeur, vous affectez une valeur à la variable dont elle est l’alias.When you assign its value, you are assigning a value to the variable it aliases.
  • Quand vous lisez sa valeur, vous lisez la valeur de la variable dont elle est l’alias.When you read its value, you are reading the value of the variable it aliases.
  • Si vous la retournez par référence, vous retournez un alias vers cette même variable.If you return it by reference, you are returning an alias to that same variable.
  • Si vous la passez à une autre méthode par référence, vous passez une référence à la variable dont elle est l’alias.If you pass it to another method by reference, you are passing a reference to the variable it aliases.
  • Quand vous créez un alias de variable locale ref, vous créez un nouvel alias vers la même variable.When you make a ref local alias, you make a new alias to the same variable.

Variables locales refRef locals

Partez du principe que la méthode GetContactInformation est déclarée comme un retour de référence :Assume the GetContactInformation method is declared as a ref return:

public ref Person GetContactInformation(string fname, string lname)

Une affectation par valeur lit la valeur d’une variable et l’affecte à une nouvelle variable :A by-value assignment reads the value of a variable and assigns it to a new variable:

Person p = contacts.GetContactInformation("Brandie", "Best");

L’affectation précédente déclare p comme une variable locale.The preceding assignment declares p as a local variable. Sa valeur initiale est copiée à partir de la lecture de la valeur retournée par GetContactInformation.Its initial value is copied from reading the value returned by GetContactInformation. Toute affectation future à p ne modifiera en rien la valeur de la variable renvoyée par GetContactInformation.Any future assignments to p will not change the value of the variable returned by GetContactInformation. La variable p n’est plus un alias vers la variable retournée.The variable p is no longer an alias to the variable returned.

Vous déclarez une variable locale ref pour copier l’alias vers la valeur d’origine.You declare a ref local variable to copy the alias to the original value. Dans l’affectation suivante, p est un alias vers la variable retournée à partir de GetContactInformation.In the following assignment, p is an alias to the variable returned from GetContactInformation.

ref Person p = ref contacts.GetContactInformation("Brandie", "Best");

L’utilisation ultérieure de p revient à utiliser la variable retournée par GetContactInformation, car p est un alias de cette variable.Subsequent usage of p is the same as using the variable returned by GetContactInformation because p is an alias for that variable. Les modifications apportées à p modifient également la variable retournée à partir de GetContactInformation.Changes to p also change the variable returned from GetContactInformation.

Le mot clé ref est utilisé à la fois avant la déclaration de la variable locale et avant l’appel de la méthode.The ref keyword is used both before the local variable declaration and before the method call.

Vous pouvez accéder à une valeur par référence de la même façon.You can access a value by reference in the same way. Dans certains cas, l’accès à une valeur par référence augmente les performances en évitant une opération de copie potentiellement coûteuse.In some cases, accessing a value by reference increases performance by avoiding a potentially expensive copy operation. Par exemple, l’instruction suivante montre comment il est possible de définir une valeur locale ref qui est utilisée pour référencer une valeur.For example, the following statement shows how one can define a ref local value that is used to reference a value.

ref VeryLargeStruct reflocal = ref veryLargeStruct;

Le mot clé ref est utilisé à la fois avant la déclaration de la variable locale et avant la valeur du second exemple.The ref keyword is used both before the local variable declaration and before the value in the second example. Si les deux mots clés ref ne sont pas inclus dans la déclaration et l’affectation de la variable dans les deux exemples, l’erreur du compilateur CS8172, « Impossible d’initialiser une variable par référence avec une valeur » est générée.Failure to include both ref keywords in the variable declaration and assignment in both examples results in compiler error CS8172, "Cannot initialize a by-reference variable with a value."

Dans les versions antérieures à C# 7.3, les variables locales ref ne pouvaient pas être réassignées pour référencer un stockage différent après avoir été initialisées.Prior to C# 7.3, ref local variables couldn't be reassigned to refer to different storage after being initialized. Cette restriction a été supprimée.That restriction has been removed. L’exemple suivant illustre une réassignation :The following example shows a reassignment:

ref VeryLargeStruct reflocal = ref veryLargeStruct; // initialization
refLocal = ref anotherVeryLargeStruct; // reassigned, refLocal refers to different storage.

Les variables locales ref doivent toujours être initialisées quand elles sont déclarées.Ref local variables must still be initialized when they are declared.

Retours ref et variables locales ref : exempleRef returns and ref locals: an example

L’exemple suivant définit une classe NumberStore qui stocke un tableau de valeurs entières.The following example defines a NumberStore class that stores an array of integer values. La méthode FindNumber retourne par référence le premier nombre supérieur ou égal au nombre passé comme argument.The FindNumber method returns by reference the first number that is greater than or equal to the number passed as an argument. Si aucun nombre n’est supérieur ou égal à l’argument, la méthode retourne le nombre se trouvant à l’index 0.If no number is greater than or equal to the argument, the method returns the number in index 0.

using System;

class NumberStore
{
    int[] numbers = { 1, 3, 7, 15, 31, 63, 127, 255, 511, 1023 };

    public ref int FindNumber(int target)
    {
        for (int ctr = 0; ctr < numbers.Length; ctr++)
        {
            if (numbers[ctr] >= target)
                return ref numbers[ctr];
        }
        return ref numbers[0];
    }

    public override string ToString() => string.Join(" ", numbers);
}

L’exemple suivant appelle la méthode NumberStore.FindNumber pour récupérer la première valeur supérieure ou égale à 16.The following example calls the NumberStore.FindNumber method to retrieve the first value that is greater than or equal to 16. L’appelant multiplie ensuite par deux la valeur retournée par la méthode.The caller then doubles the value returned by the method. La sortie de l’exemple montre comment la modification est reflétée dans la valeur des éléments de tableau de l’instance NumberStore.The output from the example shows the change reflected in the value of the array elements of the NumberStore instance.

var store = new NumberStore();
Console.WriteLine($"Original sequence: {store.ToString()}");
int number = 16;
ref var value = ref store.FindNumber(number);
value *= 2;
Console.WriteLine($"New sequence:      {store.ToString()}");
// The example displays the following output:
//       Original sequence: 1 3 7 15 31 63 127 255 511 1023
//       New sequence:      1 3 7 15 62 63 127 255 511 1023

Si les valeurs de retour de référence ne sont pas prises en charge, une telle opération est effectuée en retournant l’index de l’élément de tableau ainsi que sa valeur de retour.Without support for reference return values, such an operation is performed by returning the index of the array element along with its value. L’appelant peut ensuite utiliser cet index pour modifier la valeur dans un appel de méthode distinct.The caller can then use this index to modify the value in a separate method call. Toutefois, l’appelant peut également modifier l’index pour accéder à d’autres valeurs de tableau et éventuellement les modifier.However, the caller can also modify the index to access and possibly modify other array values.

L’exemple suivant montre comment réécrire la méthode FindNumber à compter de C# 7.3 pour utiliser la réassignation des variables locales ref :The following example shows how the FindNumber method could be rewritten after C# 7.3 to use ref local reassignment:

using System;

class NumberStore
{
    int[] numbers = { 1, 3, 7, 15, 31, 63, 127, 255, 511, 1023 };

    public ref int FindNumber(int target)
    {
        ref int returnVal = ref numbers[0];
        var ctr = numbers.Length - 1;
        while ((ctr > 0) && numbers[ctr] >= target)
        {
            returnVal = ref numbers[ctr];
            ctr--;
        }
        return ref returnVal;
    }

    public override string ToString() => string.Join(" ", numbers);
}

Cette deuxième version est plus efficace avec des séquences plus longues dans les scénarios où le nombre recherché est proche de la fin du tableau.This second version is more efficient with longer sequences in scenarios where the number sought is closer to the end of the array.

Voir aussiSee also