Partager via


Paramètres et modificateurs de méthode

Par défaut, les arguments en C# sont passés aux fonctions par valeur. Cela signifie qu’une copie de la variable est passée à la méthode. Pour les types valeur (struct), une copie de la valeur est passée à la méthode. Pour les types référence (class), une copie de la référence est passée à la méthode. Les modificateurs de paramètres vous permettent de passer des arguments par référence.

Étant donné qu’un struct est un type valeur, la méthode reçoit et fonctionne sur une copie de l’argument lorsque vous transmettez un struct par valeur à une méthode. La méthode n’a pas accès au struct d’origine dans la méthode d’appel, et ne peut donc pas le modifier de quelque manière que ce soit. La méthode peut modifier uniquement la copie.

Une instance de classe est un type référence, pas un type valeur. Lorsqu’un type référence est passé par valeur à une méthode, la méthode reçoit une copie de la référence à l’instance. Les deux variables font référence au même objet. Le paramètre est une copie de la référence. La méthode appelée ne peut pas réaffecter l’instance dans la méthode appelante. Toutefois, la méthode appelée peut utiliser la copie de la référence pour accéder aux membres de l’instance. Si la méthode appelée modifie un membre d’instance, la méthode appelante voit également ces modifications, car elle fait référence à la même instance.

Passer par valeur et passer par référence

Tous les exemples de cette section utilisent les deux record types suivants pour illustrer les différences entre class les types et struct les types :

public record struct Point(int X, int Y);
// This doesn't use a primary constructor because the properties implemented for `record` types are 
// readonly in record class types. That would prevent the mutations necessary for this example.
public record class Point3D
{
    public int X { get; set; }
    public int Y { get; set; }
    public int Z { get; set; }
}

La sortie de l’exemple suivant illustre la différence entre le passage d’un type de struct par valeur et le passage d’un type de classe par valeur. Les deux Mutate méthodes modifient les valeurs de propriété de son argument. Lorsque le paramètre est un struct type, ces modifications sont apportées à une copie des données de l’argument. Lorsque le paramètre est un class type, ces modifications sont apportées à l’instance référencée par l’argument :

public class PassTypesByValue
{
    public static void Mutate(Point pt)
    {
        Console.WriteLine($"\tEnter {nameof(Mutate)}:\t\t{pt}");
        pt.X = 19;
        pt.Y = 23;

        Console.WriteLine($"\tExit {nameof(Mutate)}:\t\t{pt}");
    }
    public static void Mutate(Point3D pt)
    {
        Console.WriteLine($"\tEnter {nameof(Mutate)}:\t\t{pt}");
        pt.X = 19;
        pt.Y = 23;
        pt.Z = 42;

        Console.WriteLine($"\tExit {nameof(Mutate)}:\t\t{pt}");
    }

    public static void TestPassTypesByValue()
    {
        Console.WriteLine("===== Value Types =====");

        var ptStruct = new Point { X = 1, Y = 2 };
        Console.WriteLine($"After initialization:\t\t{ptStruct}");

        Mutate(ptStruct);

        Console.WriteLine($"After called {nameof(Mutate)}:\t\t{ptStruct}");

        Console.WriteLine("===== Reference Types =====");

        var ptClass = new Point3D { X = 1, Y = 2, Z = 3 };

        Console.WriteLine($"After initialization:\t\t{ptClass}");

        Mutate(ptClass);
        Console.WriteLine($"After called {nameof(Mutate)}:\t\t{ptClass}");

        // Output:
        // ===== Value Types =====
        // After initialization:           Point { X = 1, Y = 2 }
        //         Enter Mutate:           Point { X = 1, Y = 2 }
        //         Exit Mutate:            Point { X = 19, Y = 23 }
        // After called Mutate:            Point { X = 1, Y = 2 }
        // ===== Reference Types =====
        // After initialization:           Point3D { X = 1, Y = 2, Z = 3 }
        //         Enter Mutate:           Point3D { X = 1, Y = 2, Z = 3 }
        //         Exit Mutate:            Point3D { X = 19, Y = 23, Z = 42 }
        // After called Mutate:            Point3D { X = 19, Y = 23, Z = 42 }
    }
}

Le ref modificateur est un moyen de passer des arguments par référence à des méthodes. Le code suivant suit l’exemple précédent, mais passe les paramètres par référence. Les modifications apportées au struct type sont visibles dans la méthode appelante lorsque le struct est passé par référence. Il n’existe aucune modification sémantique lorsqu’un type de référence est passé par référence :

public class PassTypesByReference
{
    public static void Mutate(ref Point pt)
    {
        Console.WriteLine($"\tEnter {nameof(Mutate)}:\t\t{pt}");
        pt.X = 19;
        pt.Y = 23;

        Console.WriteLine($"\tExit {nameof(Mutate)}:\t\t{pt}");
    }
    public static void Mutate(ref Point3D pt)
    {
        Console.WriteLine($"\tEnter {nameof(Mutate)}:\t\t{pt}");
        pt.X = 19;
        pt.Y = 23;
        pt.Z = 42;

        Console.WriteLine($"\tExit {nameof(Mutate)}:\t\t{pt}");
    }

    public static void TestPassTypesByReference()
    {
        Console.WriteLine("===== Value Types =====");

        var pStruct = new Point { X = 1, Y = 2 };
        Console.WriteLine($"After initialization:\t\t{pStruct}");

        Mutate(ref pStruct);

        Console.WriteLine($"After called {nameof(Mutate)}:\t\t{pStruct}");

        Console.WriteLine("===== Reference Types =====");

        var pClass = new Point3D { X = 1, Y = 2, Z = 3 };

        Console.WriteLine($"After initialization:\t\t{pClass}");

        Mutate(ref pClass);
        Console.WriteLine($"After called {nameof(Mutate)}:\t\t{pClass}");

        // Output:
        // ===== Value Types =====
        // After initialization:           Point { X = 1, Y = 2 }
        //         Enter Mutate:           Point { X = 1, Y = 2 }
        //         Exit Mutate:            Point { X = 19, Y = 23 }
        // After called Mutate:            Point { X = 19, Y = 23 }
        // ===== Reference Types =====
        // After initialization:           Point3D { X = 1, Y = 2, Z = 3 }
        //         Enter Mutate:           Point3D { X = 1, Y = 2, Z = 3 }
        //         Exit Mutate:            Point3D { X = 19, Y = 23, Z = 42 }
        // After called Mutate:            Point3D { X = 19, Y = 23, Z = 42 }
    }
}

Les exemples précédents ont modifié les propriétés d’un paramètre. Une méthode peut également réaffecter un paramètre à une nouvelle valeur. La réaffectation se comporte différemment pour les types de struct et de classe lorsqu’ils sont passés par valeur ou par référence. L’exemple suivant montre comment les types de struct et les types de classes se comportent lorsque les paramètres passés par valeur sont réaffectés :

public class PassByValueReassignment
{
    public static void Reassign(Point pt)
    {
        Console.WriteLine($"\tEnter {nameof(Reassign)}:\t\t{pt}");
        pt = new Point { X = 13, Y = 29 };

        Console.WriteLine($"\tExit {nameof(Reassign)}:\t\t{pt}");
    }

    public static void Reassign(Point3D pt)
    {
        Console.WriteLine($"\tEnter {nameof(Reassign)}:\t\t{pt}");
        pt = new Point3D { X = 13, Y = 29, Z = -42 };

        Console.WriteLine($"\tExit {nameof(Reassign)}:\t\t{pt}");
    }

    public static void TestPassByValueReassignment()
    {
        Console.WriteLine("===== Value Types =====");

        var ptStruct = new Point { X = 1, Y = 2 };
        Console.WriteLine($"After initialization:\t\t{ptStruct}");

        Reassign(ptStruct);

        Console.WriteLine($"After called {nameof(Reassign)}:\t\t{ptStruct}");

        Console.WriteLine("===== Reference Types =====");

        var ptClass = new Point3D { X = 1, Y = 2, Z = 3 };

        Console.WriteLine($"After initialization:\t\t{ptClass}");

        Reassign(ptClass);
        Console.WriteLine($"After called {nameof(Reassign)}:\t\t{ptClass}");

        // Output:
        // ===== Value Types =====
        // After initialization:           Point { X = 1, Y = 2 }
        //         Enter Reassign:         Point { X = 1, Y = 2 }
        //         Exit Reassign:          Point { X = 13, Y = 29 }
        // After called Reassign:          Point { X = 1, Y = 2 }
        // ===== Reference Types =====
        // After initialization:           Point3D { X = 1, Y = 2, Z = 3 }
        //         Enter Reassign:         Point3D { X = 1, Y = 2, Z = 3 }
        //         Exit Reassign:          Point3D { X = 13, Y = 29, Z = -42 }
        // After called Reassign:          Point3D { X = 1, Y = 2, Z = 3 }
    }
}

L’exemple précédent montre que lorsque vous réaffectez un paramètre à une nouvelle valeur, cette modification n’est pas visible à partir de la méthode appelante, que le type soit un type valeur ou un type référence. L’exemple suivant montre le comportement lorsque vous réaffectez un paramètre qui a été passé par référence :

public class PassByReferenceReassignment
{
    public static void Reassign(ref Point pt)
    {
        Console.WriteLine($"\tEnter {nameof(Reassign)}:\t\t{pt}");
        pt = new Point { X = 13, Y = 29 };

        Console.WriteLine($"\tExit {nameof(Reassign)}:\t\t{pt}");
    }

    public static void Reassign(ref Point3D pt)
    {
        Console.WriteLine($"\tEnter {nameof(Reassign)}:\t\t{pt}");
        pt = new Point3D { X = 13, Y = 29, Z = -42 };

        Console.WriteLine($"\tExit {nameof(Reassign)}:\t\t{pt}");
    }

    public static void TestPassByReferenceReassignment()
    {
        Console.WriteLine("===== Value Types =====");

        var ptStruct = new Point { X = 1, Y = 2 };
        Console.WriteLine($"After initialization:\t\t{ptStruct}");

        Reassign(ref ptStruct);

        Console.WriteLine($"After called {nameof(Reassign)}:\t\t{ptStruct}");

        Console.WriteLine("===== Reference Types =====");

        var ptClass = new Point3D { X = 1, Y = 2, Z = 3 };

        Console.WriteLine($"After initialization:\t\t{ptClass}");

        Reassign(ref ptClass);
        Console.WriteLine($"After called {nameof(Reassign)}:\t\t{ptClass}");

        // Output:
        // ===== Value Types =====
        // After initialization:           Point { X = 1, Y = 2 }
        //         Enter Reassign:         Point { X = 1, Y = 2 }
        //         Exit Reassign:          Point { X = 13, Y = 29 }
        // After called Reassign:          Point { X = 13, Y = 29 }
        // ===== Reference Types =====
        // After initialization:           Point3D { X = 1, Y = 2, Z = 3 }
        //         Enter Reassign:         Point3D { X = 1, Y = 2, Z = 3 }
        //         Exit Reassign:          Point3D { X = 13, Y = 29, Z = -42 }
        // After called Reassign:          Point3D { X = 13, Y = 29, Z = -42 }
    }
}

L’exemple précédent montre comment réaffecter la valeur d’un paramètre passé par référence est visible dans le contexte appelant.

Contexte sécurisé des références et des valeurs

Les méthodes peuvent stocker les valeurs des paramètres dans des champs. Lorsque les paramètres sont passés par valeur, cela est généralement fait de façon sécurisée. Les valeurs sont copiées et les types de référence sont accessibles lorsqu’ils sont stockés dans un champ. La transmission de paramètres par référence de façon sûre nécessite que le compilateur définisse quand il est sûr d’affecter une référence à une nouvelle variable. Pour chaque expression, le compilateur définit un contexte sécurisé qui limite l’accès à une expression ou à une variable. Le compilateur utilise deux étendues : safe-context et ref-safe-context.

  • L’étendue safe-context définit l’étendue dans laquelle n’importe quelle expression est accessible en toute sécurité.
  • L’étendue ref-safe-context définit l’étendue dans laquelle une référence à n’importe quelle expression peut être accessible ou modifiée en toute sécurité.

De manière informelle, vous pouvez considérer ces étendues comme le mécanisme permettant de vous assurer que votre code n’accède jamais ou ne modifie jamais une référence qui n’est plus valide. Une référence est valide tant qu’elle fait référence à un objet ou à un struct valide. L’étendue safe-context définit quand une variable peut être affectée ou réaffectée. L’étendue ref-safe-context définit quand une variable peut être affectée par référence ou réaffectée par référence. L’affectation affecte une variable à une nouvelle valeur ; l’affectation par référence affecte la variable pour faire référence à un autre emplacement de stockage.

Paramètres de référence

Vous appliquez l’un des modificateurs suivants à une déclaration de paramètre pour passer des arguments par référence plutôt que par valeur :

  • ref : l’argument doit être initialisé avant d’appeler la méthode. La méthode peut affecter une nouvelle valeur au paramètre, mais n’est pas obligée de le faire.
  • out : la méthode appelante n’est pas obligée d’initialiser l’argument avant d’appeler la méthode. La méthode doit affecter une valeur au paramètre.
  • ref readonly : l’argument doit être initialisé avant d’appeler la méthode. La méthode ne peut pas affecter de nouvelle valeur au paramètre.
  • in : l’argument doit être initialisé avant d’appeler la méthode. La méthode ne peut pas affecter de nouvelle valeur au paramètre. Le compilateur peut créer une variable temporaire pour contenir une copie de l’argument pour les paramètres in.

Un paramètre passé par référence est une variable de référence. Elle n’a pas sa propre valeur. Au lieu de cela, il fait référence à une variable différente appelée son referrent. Les variables de référence peuvent être réaffectées, ce qui modifie son réaffectation.

Les membres d’une classe ne peuvent pas avoir de signatures qui diffèrent uniquement par ref, ref readonly, in ou out. Une erreur de compilation se produit si la seule différence entre deux membres d’un type est que l’un d’eux a un paramètre ref et que l’autre un a un paramètre out, ref readonly ou in. Toutefois, les méthodes peuvent être surchargées quand une méthode a un paramètre ref, ref readonly, in, ou out et que l’autre a un paramètre transmis par valeur, comme illustré dans l’exemple suivant. Dans d'autres situations nécessitant une correspondance de signature, comme le masquage ou la substitution, in, ref, ref readonly et out font partie de la signature et ne correspondent pas l’un à l’autre.

Lorsqu’un paramètre a l’un des modificateurs précédents, l’argument correspondant peut avoir un modificateur compatible :

  • Un argument pour un paramètre ref doit inclure le modificateur ref.
  • Un argument pour un paramètre out doit inclure le modificateur out.
  • Un argument pour un paramètre in peut éventuellement inclure le modificateur in. Si le modificateur ref est utilisé sur l’argument à la place, le compilateur émet un avertissement.
  • Un argument pour un paramètre ref readonly doit inclure soit le modificateur in, soit le ref, mais pas les deux. Si aucun modificateur n’est inclus, le compilateur émet un avertissement.

Lorsque vous utilisez ces modificateurs, ils décrivent comment l’argument est utilisé :

  • ref signifie que la méthode peut lire ou écrire la valeur de l’argument.
  • out signifie que la méthode définit la valeur de l’argument.
  • ref readonly signifie que la méthode lit, mais ne peut pas écrire la valeur de l’argument. L’argument doit être passé par référence.
  • in signifie que la méthode lit, mais ne peut pas écrire la valeur de l’argument. L’argument est passé par référence ou par le biais d’une variable temporaire.

Vous ne pouvez pas utiliser les modificateurs de paramètres précédents dans les types de méthodes suivants :

  • Méthodes async, que vous définissez à l’aide du modificateur async.
  • Les méthodes Iterator, qui incluent une instruction yield return ou yield break.

Les méthodes d’extension ont également des restrictions sur l’utilisation de ces mots clés d’argument :

  • Le out mot clé ne peut pas être utilisé sur le premier argument d’une méthode d’extension.
  • Le mot clé ref ne peut pas être utilisé sur le premier argument d’une méthode d’extension lorsque l’argument n’est pas un struct, ou qu’un type générique n’est pas contraint d’être un struct.
  • Les mots clés ref readonly et in ne peuvent pas être utilisés, sauf si le premier argument est un struct.
  • Les mots clés ref readonly et in ne peuvent pas être utilisés sur n’importe quel type générique, même s’ils sont contraints d’être un struct.

Les propriétés ne sont pas des variables. Ce sont des méthodes. Les propriétés ne peuvent pas être des arguments pour les paramètres ref.

Modificateur de paramètre ref

Pour utiliser un paramètre ref, la définition de la méthode et la méthode d'appel doivent utiliser explicitement le mot clé ref, comme indiqué dans l'exemple suivant. (Sauf que la méthode appelante peut omettre ref lors de l’appel COM.)

void Method(ref int refArgument)
{
    refArgument = refArgument + 44;
}

int number = 1;
Method(ref number);
Console.WriteLine(number);
// Output: 45

Un argument passé à un paramètre ref doit être initialisé avant d'être transmis.

Modificateur de paramètre out

Pour utiliser un paramètre out, la définition de la méthode et la méthode d'appel doivent utiliser explicitement le mot clé out. Par exemple :

int initializeInMethod;
OutArgExample(out initializeInMethod);
Console.WriteLine(initializeInMethod);     // value is now 44

void OutArgExample(out int number)
{
    number = 44;
}

Les variables passées comme arguments out n’ont pas à être initialisées avant d’être passées dans un appel de méthode. Toutefois, la méthode appelée doit assigner une valeur avant le retour de la méthode.

Les méthodes deconstruct déclarent leurs paramètres avec le modificateur out pour retourner plusieurs valeurs. D’autres méthodes peuvent retourner des tuples de valeur pour plusieurs valeurs renvoyées.

Vous pouvez déclarer une variable dans une instruction distincte avant de la passer en tant qu’outargument. Vous pouvez également déclarer la out variable dans la liste d’arguments de l’appel de méthode au lieu de le faire dans une déclaration de variable distincte. Les déclarations de variable out rendent le code plus simple et plus lisible, et vous empêchent également d’assigner par inadvertance une valeur à la variable avant l’appel de méthode. L’exemple suivant définit la variable number dans l’appel de la méthode Int32.TryParse.

string numberAsString = "1640";

if (Int32.TryParse(numberAsString, out int number))
    Console.WriteLine($"Converted '{numberAsString}' to {number}");
else
    Console.WriteLine($"Unable to convert '{numberAsString}'");
// The example displays the following output:
//       Converted '1640' to 1640

Vous pouvez également déclarer une variable locale implicitement typée.

Modificateur ref readonly

Le modificateur ref readonly doit être présent dans la déclaration de méthode. Un modificateur sur le site d’appel est facultatif. Vous pouvez utiliser le modificateur in ou ref. Le modificateur ref readonly n’est pas valide sur le site d’appel. Le modificateur que vous utilisez sur le site d’appel peut vous aider à décrire les caractéristiques de l’argument. Vous ne pouvez utiliser ref que si l’argument est une variable et est accessible en écriture. Vous ne pouvez utiliser in que lorsque l’argument est une variable. Il peut être accessible en écriture ou en lecture seule. Vous ne pouvez pas ajouter ces modificateurs si l’argument n’est pas une variable, mais qu’il s’agit d’une expression. Les exemples suivants illustrent ces conditions. La méthode suivante utilise le modificateur ref readonly pour indiquer qu’un struct volumineux doit être passé par référence pour des raisons de performances :

public static void ForceByRef(ref readonly OptionStruct thing)
{
    // elided
}

Vous pouvez appeler la méthode à l’aide du modificateur ref ou in. Si vous omettez le modificateur, le compilateur émet un avertissement. Lorsque l’argument est une expression, et non une variable, vous ne pouvez pas ajouter les modificateurs in ou ref. Vous devez donc supprimer l’avertissement :

ForceByRef(in options);
ForceByRef(ref options);
ForceByRef(options); // Warning! variable should be passed with `ref` or `in`
ForceByRef(new OptionStruct()); // Warning, but an expression, so no variable to reference

Si la variable est une variable readonly, vous devez utiliser le modificateur in. Le compilateur émet une erreur si vous utilisez le modificateur ref à la place.

Le modificateur ref readonly indique que la méthode s’attend à ce que l’argument soit une variable plutôt qu’une expression qui n’est pas une variable. Des constantes, des valeurs de retour de méthode et des propriétés sont des exemples d’expressions qui ne sont pas des variables. Si l’argument n’est pas une variable, le compilateur émet un avertissement.

Modificateur de paramètre in

Le modificateur in est requis dans la déclaration de méthode, mais inutile sur le site d’appel.

var largeStruct = new LargeStruct { Value1 = 42, Value2 = 3.14, Value3 = "Hello" };

// Using 'in' avoids copying the large struct and prevents modification
ProcessLargeStruct(in largeStruct);
Console.WriteLine($"Original value unchanged: {largeStruct.Value1}");

// Without 'in', the struct would be copied (less efficient for large structs)
ProcessLargeStructByValue(largeStruct);
Console.WriteLine($"Original value still unchanged: {largeStruct.Value1}");

void ProcessLargeStruct(in LargeStruct data)
{
    // Can read the values
    Console.WriteLine($"Processing: {data.Value1}, {data.Value2}, {data.Value3}");
    
    // Uncomment the following line to see error CS8331
    // data.Value1 = 99; // Compilation error: cannot assign to 'in' parameter
}

void ProcessLargeStructByValue(LargeStruct data)
{
    // This method receives a copy of the struct
    Console.WriteLine($"Processing copy: {data.Value1}, {data.Value2}, {data.Value3}");
    
    // Modifying the copy doesn't affect the original
    data.Value1 = 99;
}

Le modificateur in permet au compilateur de créer une variable temporaire pour l’argument et de passer une référence en lecture seule à cet argument. Le compilateur crée toujours une variable temporaire lorsque l’argument doit être converti, lorsqu’il existe une conversion implicite à partir du type d’argument ou lorsque l’argument est une valeur qui n’est pas une variable. Par exemple, lorsque l’argument est une valeur littérale ou la valeur retournée à partir d’un accesseur de propriété. Lorsque votre API nécessite que l’argument soit passé par référence, choisissez le modificateur ref readonly et non le in.

Les méthodes définies à l’aide des paramètres in peuvent potentiellement optimiser les performances. Certains arguments de type struct peuvent être volumineux et, quand les méthodes sont appelées dans des boucles serrées ou des chemins de code critiques, le coût de la copie de ces structures est important. Les méthodes déclarent des paramètres in pour spécifier que des arguments peuvent être passés par référence en toute sécurité, car la méthode appelée ne modifie pas l’état de l’argument. Le passage de ces arguments par référence permet d’éviter une copie (potentiellement) coûteuse. Vous ajoutez explicitement le modificateur in sur le site d’appel pour vérifier que l’argument est passé par référence, non par valeur. L’utilisation explicite de in a les deux effets suivants :

  • La spécification de in sur le site d’appel force le compilateur à sélectionner une méthode définie avec un paramètre in correspondant. Sinon, quand deux méthodes diffèrent uniquement par la présence de in, la surcharge par valeur convient mieux.
  • En spécifiant in, vous déclarez votre intention de passer un argument par référence. L’argument utilisé avec in doit représenter un emplacement directement référençable. Les mêmes règles générales pour les arguments out et ref s’appliquent : vous ne pouvez pas utiliser de constantes, de propriétés ordinaires ou d’autres expressions qui produisent des valeurs. Sinon, l’omission de in sur le site d’appel informe le compilateur qu’il est autorisé à créer une variable temporaire à passer par référence en lecture seule à la méthode. Le compilateur crée une variable temporaire pour surmonter plusieurs restrictions avec les arguments in :
    • Une variable temporaire autorise des constantes au moment de la compilation comme paramètres in.
    • Une variable temporaire autorise des propriétés ou d’autres expressions pour les paramètres in.
    • Une variable temporaire autorise des arguments pour lesquels il existe une conversion implicite de type d’argument en type de paramètre.

Dans toutes les instances précédentes, le compilateur crée une variable temporaire qui stocke la valeur de la constante, la propriété ou une autre expression.

Le code suivant illustre ces règles :

static void Method(in int argument)
{
    // implementation removed
}

Method(5); // OK, temporary variable created.
Method(5L); // CS1503: no implicit conversion from long to int
short s = 0;
Method(s); // OK, temporary int created with the value 0
Method(in s); // CS1503: cannot convert from in short to in int
int i = 42;
Method(i); // passed by readonly reference
Method(in i); // passed by readonly reference, explicitly using `in`

Supposons à présent qu’une autre méthode utilisant des arguments par valeur est disponible. Les résultats changent comme illustré dans le code suivant :

static void Method(int argument)
{
    // implementation removed
}

static void Method(in int argument)
{
    // implementation removed
}

Method(5); // Calls overload passed by value
Method(5L); // CS1503: no implicit conversion from long to int
short s = 0;
Method(s); // Calls overload passed by value.
Method(in s); // CS1503: cannot convert from in short to in int
int i = 42;
Method(i); // Calls overload passed by value
Method(in i); // passed by readonly reference, explicitly using `in`

Le seul appel de méthode dans lequel l’argument est passé par référence est le dernier.

Notes

Le code précédent utilise int comme type d’argument par souci de simplicité. Comme int n’est pas plus volumineux qu’une référence dans la plupart des ordinateurs modernes, il n’y aucun avantage à passer un seul int comme référence en lecture seule.

Modificateur params

Dans une déclaration de méthode, vous ne pouvez pas spécifier d’autre paramètre après le mot clé params et vous pouvez utiliser un seul mot clé params.

Le type déclaré du paramètre params doit être un type collection. Les types collection reconnus sont :

Avant C# 13, le paramètre doit être un tableau unidimensionnel.

Lorsque vous appelez une méthode avec un paramètre params, vous pouvez passer :

  • Une liste séparée par des virgules d’arguments du type des éléments de tableau.
  • Une collection d’arguments du type spécifié.
  • Aucun argument. Si vous n’envoyez aucun argument, la longueur de la liste params est égale à zéro.

L’exemple suivant montre différentes façons d’envoyer des arguments à un paramètre params.

public static void ParamsModifierExample(params int[] list)
{
    for (int i = 0; i < list.Length; i++)
    {
        System.Console.Write(list[i] + " ");
    }
    System.Console.WriteLine();
}

public static void ParamsModifierObjectExample(params object[] list)
{
    for (int i = 0; i < list.Length; i++)
    {
        System.Console.Write(list[i] + " ");
    }
    System.Console.WriteLine();
}

public static void TryParamsCalls()
{
    // You can send a comma-separated list of arguments of the
    // specified type.
    ParamsModifierExample(1, 2, 3, 4);
    ParamsModifierObjectExample(1, 'a', "test");

    // A params parameter accepts zero or more arguments.
    // The following calling statement displays only a blank line.
    ParamsModifierObjectExample();

    // An array argument can be passed, as long as the array
    // type matches the parameter type of the method being called.
    int[] myIntArray = { 5, 6, 7, 8, 9 };
    ParamsModifierExample(myIntArray);

    object[] myObjArray = { 2, 'b', "test", "again" };
    ParamsModifierObjectExample(myObjArray);

    // The following call causes a compiler error because the object
    // array cannot be converted into an integer array.
    //ParamsModifierExample(myObjArray);

    // The following call does not cause an error, but the entire
    // integer array becomes the first element of the params array.
    ParamsModifierObjectExample(myIntArray);
}
/*
Output:
    1 2 3 4
    1 a test

    5 6 7 8 9
    2 b test again
    System.Int32[]
*/

La résolution de surcharge peut entraîner une ambiguïté lorsque l’argument d’un params paramètre est un type de collection. Le type de collection de l’argument doit être convertible en type de collection du paramètre. Lorsque différentes surcharges fournissent de meilleures conversions pour ce paramètre, cette méthode peut être meilleure. Toutefois, si l’argument du params paramètre est des éléments discrets ou manquants, toutes les surcharges avec différents params types de paramètres sont égales à ce paramètre.

Pour plus d’informations, consultez la section sur les listes d’arguments dans la spécification du langage C#. La spécification du langage est la source de référence pour la syntaxe C# et son utilisation.