Notes
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
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ètresin
.
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 modificateurref
. - Un argument pour un paramètre
out
doit inclure le modificateurout
. - Un argument pour un paramètre
in
peut éventuellement inclure le modificateurin
. Si le modificateurref
est utilisé sur l’argument à la place, le compilateur émet un avertissement. - Un argument pour un paramètre
ref readonly
doit inclure soit le modificateurin
, soit leref
, 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 unstruct
, ou qu’un type générique n’est pas contraint d’être un struct. - Les mots clés
ref readonly
etin
ne peuvent pas être utilisés, sauf si le premier argument est unstruct
. - Les mots clés
ref readonly
etin
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’out
argument. 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ètrein
correspondant. Sinon, quand deux méthodes diffèrent uniquement par la présence dein
, 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é avecin
doit représenter un emplacement directement référençable. Les mêmes règles générales pour les argumentsout
etref
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 dein
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 argumentsin
:- 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.
- Une variable temporaire autorise des constantes au moment de la compilation comme paramètres
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 :
- Un type tableau
T[]
unidimensionnel, auquel cas le type élément estT
. - Un type étendue :
System.Span<T>
System.ReadOnlySpan<T>
Ici, le type élément estT
.
- Un type avec une méthode create accessible avec un type élément correspondant. La méthode create est identifiée en utilisant le même attribut que celui utilisé pour les expressions de collection.
- Un struct ou un type classe qui implémente System.Collections.Generic.IEnumerable<T>, où :
- Le type a un constructeur qui peut être appelé sans arguments, et le constructeur est au moins aussi accessible que le membre déclarant.
- Le type a une méthode (pas une extension) d’instance
Add
, où :- La méthode peut être appelée avec un seul argument de valeur.
- Si la méthode est générique, les arguments de type peuvent être inférés de l’argument.
- La méthode est au moins aussi accessible que le membre déclarant. Ici, le type élément est le type itération du type.
- Un type interface :
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.