Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Per impostazione predefinita, C# passa argomenti alle funzioni per valore. Questo approccio passa una copia della variabile al metodo . Per i tipi valore (struct), il metodo ottiene una copia del valore. Per i tipi riferimento (class), il metodo ottiene una copia del riferimento. È possibile usare i modificatori di parametri per passare argomenti per riferimento.
Poiché uno struct è un tipo valore, passando uno struct per valore a un metodo invia una copia dell'argomento al metodo . Il metodo funziona con questa copia. Il metodo non può accedere allo struct originale nel metodo chiamante e non può modificarlo. Il metodo può modificare solo la copia.
Un'istanza di classe è un tipo riferimento, non un tipo valore. Quando si passa un tipo di riferimento per valore a un metodo, il metodo ottiene una copia del riferimento all'istanza di . Entrambe le variabili fanno riferimento allo stesso oggetto. Il parametro è una copia del riferimento. Il metodo chiamato non può riassegnare l'istanza nel metodo chiamante. Tuttavia, il metodo chiamato può usare la copia del riferimento per accedere ai membri dell'istanza. Se il metodo chiamato modifica un membro dell'istanza, il metodo chiamante visualizza anche tale modifica perché fa riferimento alla stessa istanza.
Il riferimento al linguaggio C# documenta la versione rilasciata più di recente del linguaggio C#. Contiene anche la documentazione iniziale per le funzionalità nelle versioni di anteprima pubblica per la prossima versione del linguaggio di programmazione.
La documentazione identifica tutte le funzionalità introdotte nelle ultime tre versioni della lingua o nelle anteprime pubbliche correnti.
Suggerimento
Per trovare quando una funzionalità è stata introdotta per la prima volta in C#, vedere l'articolo sulla cronologia delle versioni del linguaggio C#.
Passare per valore e passare per riferimento
Tutti gli esempi in questa sezione usano i due record tipi seguenti per illustrare le differenze tra class tipi e struct tipi:
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; }
}
L'output dell'esempio seguente illustra la differenza tra il passaggio di un tipo struct per valore e il passaggio di un tipo di classe per valore. Entrambi i Mutate metodi modificano i valori delle proprietà dell'argomento. Quando il parametro è un struct tipo, tali modifiche influiscono su una copia dei dati dell'argomento. Quando il parametro è un class tipo, tali modifiche influiscono sull'istanza a cui fa riferimento l'argomento :
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 }
}
}
Il ref modificatore è un modo per passare argomenti per riferimento ai metodi. Il codice seguente replica l'esempio precedente, ma passa i parametri per riferimento. Le modifiche apportate al struct tipo sono visibili nel metodo chiamante quando lo struct viene passato per riferimento. Non viene apportata alcuna modifica semantica quando viene passato un tipo riferimento per riferimento:
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 }
}
}
Negli esempi precedenti sono state modificate le proprietà di un parametro. Un metodo può anche riassegnare un parametro a un nuovo valore. La riassegnazione si comporta in modo diverso per i tipi di struct e di classe quando vengono passati per valore o per riferimento. Nell'esempio seguente viene illustrato il comportamento dei tipi di struct e dei tipi di classe quando vengono riassegnati i parametri passati per valore:
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'esempio precedente mostra che quando si riassegna un parametro a un nuovo valore, tale modifica non è visibile dal metodo chiamante, indipendentemente dal fatto che il tipo sia un tipo valore o un tipo riferimento. Nell'esempio seguente viene illustrato il comportamento quando si riassegna un parametro ricevuto dal metodo per riferimento:
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 }
}
}
Nell'esempio precedente viene illustrato come riassegnare il valore di un parametro passato per riferimento è visibile nel contesto chiamante.
Contesto sicuro di riferimenti e valori
I metodi possono archiviare i valori dei parametri nei campi. Quando si passano parametri per valore, in genere è sicuro. Il metodo copia i valori e i tipi riferimento sono raggiungibili quando il metodo li archivia in un campo. Il passaggio sicuro dei parametri per riferimento richiede al compilatore di definire quando è sicuro assegnare un riferimento a una nuova variabile. Per ogni espressione, il compilatore definisce un contesto sicuro che delimita l'accesso a un'espressione o a una variabile. Il compilatore usa due ambiti: safe-context e ref-safe-context.
- Safe-context definisce l'ambito in cui è possibile accedere a qualsiasi espressione in modo sicuro.
- Ref-safe-context definisce l'ambito in cui è possibile accedere o modificare in modo sicuro un riferimento a qualsiasi espressione.
In modo informale, è possibile considerare questi ambiti come il meccanismo per garantire che il codice modifichi o acceda mai a un riferimento non più valido. Un riferimento è valido purché faccia riferimento a un oggetto o a uno struct valido. Safe-context definisce quando è possibile assegnare o riassegnare una variabile. Ref-safe-context definisce quando una variabile può essere assegnata ref o riassegnata ref. L'assegnazione assegna una variabile a un nuovo valore; assegnazione ref assegna la variabile per fare riferimento a un percorso di archiviazione diverso.
Parametri di riferimento
Per passare argomenti per riferimento anziché per valore, usare uno dei modificatori seguenti in una dichiarazione di parametro:
-
ref: inizializza l'argomento prima di chiamare il metodo . Il metodo può assegnare un nuovo valore al parametro , ma non è necessario. -
out: il metodo chiamante non deve inizializzare l'argomento prima di chiamare il metodo . Il metodo deve assegnare un valore al parametro. -
ref readonly: inizializza l'argomento prima di chiamare il metodo . Il metodo non può assegnare un nuovo valore al parametro. -
in: inizializza l'argomento prima di chiamare il metodo . Il metodo non può assegnare un nuovo valore al parametro. Il compilatore potrebbe creare una variabile temporanea per contenere una copia dell'argomento dei parametriin.
Un parametro passato per riferimento è una variabile di riferimento. Non ha un proprio valore. Si riferisce invece a una variabile diversa denominata referenziale. È possibile riassegnare le variabili di riferimento, che ne modificano il referenziale.
I membri di una classe non possono avere firme che differiscono solo per ref, ref readonly, in o out. Un errore del compilatore si verifica se l'unica differenza tra due membri di un tipo è che un membro ha un parametro e l'altro membro ha un refoutparametro , ref readonlyo in . Tuttavia, è possibile eseguire l'overload dei metodi quando un metodo ha un refparametro , ref readonly, ino out e l'altro metodo ha un parametro passato per valore, come illustrato nell'esempio seguente. In altre situazioni che richiedono la firma corrispondente, ad esempio nascondere o sottoporre a override, in, ref, ref readonly e out fanno parte della firma e non sono corrispondenti tra loro.
Quando un parametro ha uno dei modificatori precedenti, l'argomento corrispondente può avere un modificatore compatibile:
- Un argomento per un parametro
refdeve includere il modificatoreref. - Un argomento per un parametro
outdeve includere il modificatoreout. - Un argomento per un parametro
inpuò facoltativamente includere il modificatorein. Se invece il modificatorerefviene usato nell'argomento, il compilatore genera un avviso. - Un argomento per un parametro
ref readonlydeve includere i modificatoriinoref, ma non entrambi. Se non è incluso alcun modificatore, il compilatore genera un avviso.
Quando si usano questi modificatori, descrivono come viene usato l'argomento:
-
refindica che il metodo può leggere o scrivere il valore dell'argomento. -
outindica che il metodo imposta il valore dell'argomento. -
ref readonlyindica che il metodo legge, ma non può scrivere il valore dell'argomento. L'argomento deve essere passato per riferimento. -
inindica che il metodo legge, ma non può scrivere il valore dell'argomento. L'argomento viene passato per riferimento o tramite una variabile temporanea.
Non è possibile usare i modificatori di parametri precedenti nei tipi di metodi seguenti:
- Metodi asincroni definiti usando il modificatore async.
- Metodi Iterator, che comprendono un'istruzione yield return o
yield break.
I membri dell'estensione hanno anche restrizioni sull'uso di queste parole chiave di argomento:
- La parola chiave
outnon può essere usata nel primo argomento di un metodo di estensione. - La parola chiave
refnon può essere usata nel primo argomento di un metodo di estensione quando l'argomento non è unstructo un tipo generico non vincolato a essere uno struct. - Le parole chiave
ref readonlyeinnon possono essere usate a meno che il primo argomento non siastruct. - Le parole chiave
ref readonlyeinnon possono essere usate in alcun tipo generico, anche se vincolate a uno struct.
Le proprietà non sono variabili. Sono metodi. Non è possibile usare le proprietà come argomenti per ref i parametri.
Modificatore di parametri ref
Per usare un parametro ref, la definizione del metodo e il metodo chiamante devono usare in modo esplicito la parola chiave ref, come illustrato nell'esempio seguente. (Ad eccezione del fatto che il metodo chiamante può omettere ref quando si effettua una chiamata COM.)
void Method(ref int refArgument)
{
refArgument = refArgument + 44;
}
int number = 1;
Method(ref number);
Console.WriteLine(number);
// Output: 45
È necessario inizializzare un argomento prima di passarlo a un ref parametro.
Modificatore di parametri out
Per usare un parametro out, la definizione del metodo e il metodo chiamante devono usare in modo esplicito la parola chiave out. Ad esempio:
int initializeInMethod;
OutArgExample(out initializeInMethod);
Console.WriteLine(initializeInMethod); // value is now 44
void OutArgExample(out int number)
{
number = 44;
}
Non è necessario inizializzare le variabili passate come out argomenti prima della chiamata al metodo. Tuttavia, il metodo chiamato deve assegnare un valore prima che venga restituito.
I metodi di decostruzione dichiarano i relativi parametri con il modificatore out per restituire più valori. Altri metodi possono restituire tuple di valori per più valori restituiti.
È possibile dichiarare una variabile in un'istruzione separata prima di passarla come argomento out. È anche possibile dichiarare la variabile out nell'elenco di argomenti della chiamata al metodo, anziché in una dichiarazione di variabile separata. Le dichiarazioni di variabile out producono codici più compatti e leggibili e impediscono l'assegnazione accidentale di un valore alla variabile prima della chiamata al metodo. Nell'esempio seguente viene definita la variabile number nella chiamata al metodo 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
È anche possibile dichiarare una variabile locale tipizzata in modo implicito.
Modificatore ref readonly
La dichiarazione del metodo richiede il ref readonly modificatore. Un modificatore nel sito di chiamata è facoltativo. È possibile usare il in modificatore o ref . Il modificatore ref readonly non è valido nel sito di chiamata. Il modificatore usato nel sito di chiamata può aiutare a descrivere le caratteristiche dell'argomento. È possibile usare ref solo se l'argomento è una variabile ed è scrivibile. È possibile usare in solo quando l'argomento è una variabile. La variabile potrebbe essere scrivibile o readonly. Non è possibile aggiungere alcun modificatore se l'argomento non è una variabile ma è un'espressione. Gli esempi seguenti mostrano queste condizioni. Il metodo seguente usa il modificatore ref readonly per indicare che uno struct di grandi dimensioni deve essere passato per riferimento per motivi di prestazioni:
public static void ForceByRef(ref readonly OptionStruct thing)
{
// elided
}
È possibile chiamare il metodo usando il ref modificatore o in . Se si omette il modificatore, il compilatore genera un avviso. Quando l'argomento è un'espressione, non una variabile, non è possibile aggiungere i modificatori in o ref, pertanto è consigliabile eliminare l'avviso:
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
Se la variabile è una variabile readonly, è necessario usare il modificatore in. Se invece si usa il modificatore ref, il compilatore genera un errore.
Il modificatore ref readonly indica che il metodo prevede che l'argomento sia una variabile anziché un'espressione che non è una variabile. Esempi di espressioni che non sono variabili sono costanti, valori restituiti dal metodo e proprietà. Se l'argomento non è una variabile, il compilatore genera un avviso.
Modificatore di parametri in
Il modificatore in è necessario nella dichiarazione del metodo, ma non nel sito di chiamata.
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;
}
Il in modificatore consente al compilatore di creare una variabile temporanea per l'argomento e di passare un riferimento di sola lettura a tale argomento. Il compilatore crea sempre una variabile temporanea quando l'argomento deve essere convertito, quando è presente una conversione implicita dal tipo di argomento o quando l'argomento è un valore che non è una variabile. Ad esempio, quando l'argomento è un valore letterale o il valore restituito da una funzione di accesso a una proprietà. Quando l'API richiede che l'argomento venga passato per riferimento, scegliere il modificatore ref readonly anziché il modificatore in.
È possibile ottenere l'ottimizzazione delle prestazioni definendo metodi con in parametri. Alcuni struct argomenti di tipo possono avere dimensioni elevate e, quando si chiamano metodi in cicli ristretti o percorsi di codice critici, il costo della copia di tali strutture è sostanziale. Dichiarare in i parametri per specificare che è possibile passare in modo sicuro gli argomenti per riferimento perché il metodo chiamato non modifica lo stato di tale argomento. Il passaggio di tali argomenti per riferimento consente di evitare una copia potenzialmente dispendiosa. Si aggiunge il modificatore in in modo esplicito presso il sito di chiamata per assicurarsi che l'argomento venga passato per riferimento, non per valore. L'uso di in in modo esplicito ha i due effetti seguenti:
- Se si specifica
inpresso il sito di chiamata si impone al compilatore di selezionare un metodo definito con un parametroincorrispondente. In caso contrario, se due metodi si differenziano solo per la presenza diin, l'overload per valore rappresenta una corrispondenza migliore. - Specificando
in, si dichiara l'intenzione di passare un argomento per riferimento. L'argomento usato conindeve rappresentare una posizione a cui sia possibile fare riferimento direttamente. Sono valide le stesse regole generali diouteref: non non è possibile usare costanti, proprietà ordinarie o altre espressioni che producono valori. In caso contrario, l'omissione diinpresso il sito di chiamata informa il compilatore che è consentito creare una variabile temporanea da passare per riferimento di sola lettura al metodo. Il compilatore crea una variabile temporanea per superare diverse restrizioni degli argomentiin:- Una variabile temporanea consente costanti in fase di compilazione come parametri
in. - Una variabile temporanea consente proprietà o altre espressioni per i parametri
in. - Una variabile temporanea consente argomenti che includono una conversione implicita dal tipo di argomento al tipo di parametro.
- Una variabile temporanea consente costanti in fase di compilazione come parametri
In tutte le istanze precedenti, il compilatore crea una variabile temporanea che archivia il valore della costante, della proprietà o di un'altra espressione.
Il codice seguente illustra queste regole:
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`
Si supponga a questo punto che sia disponibile un altro metodo che usa argomenti per valore. I risultati cambiano, come illustrato nel codice seguente:
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`
L'unica chiamata a un metodo in cui l'argomento viene passato per riferimento è quella finale.
Nota
Per semplicità, il codice precedente usa int come tipo di argomento. Poiché int non è più grande di un riferimento nella maggior parte dei computer moderni, non esiste alcun vantaggio per passare un singolo int riferimento come riferimento di sola lettura.
Modificatore params
Il parametro con la params parola chiave deve essere l'ultimo parametro nella dichiarazione del metodo. È possibile usare una params sola parola chiave in una dichiarazione di metodo.
È necessario dichiarare il params parametro come tipo di raccolta. I tipi di raccolta riconosciuti includono:
-
Tipo di
T[]matrice unidimensionale, in cui il tipo di elemento èT. - Un tipo di intervallo:
System.Span<T>System.ReadOnlySpan<T>
In questi tipi il tipo di elemento èT.
- Tipo con un metodo create accessibile con un tipo di elemento corrispondente. Il metodo create usa lo stesso attributo delle espressioni di raccolta.
- Uno struct o un tipo di classe che implementa System.Collections.Generic.IEnumerable<T> dove:
- Il tipo ha un costruttore che è possibile richiamare senza argomenti e il costruttore è almeno accessibile come membro dichiarante.
- Il tipo dispone di un metodo di istanza, non un’estensione,
Adddove:- Il metodo può essere richiamato con un singolo argomento di valore.
- Se il metodo è generico, gli argomenti di tipo possono essere dedotti dall’argomento.
- Il metodo è tanto accessibile quanto il membro dichiarante. In questo caso, il tipo di elemento è il tipo di iterazione del tipo .
- Un tipo di interfaccia:
Prima di C# 13, è necessario usare una matrice unidimensionale per il parametro .
Quando si chiama un metodo con un parametro params, è possibile passare:
- Un elenco delimitato da virgole di argomenti del tipo degli elementi della matrice.
- Raccolta di argomenti del tipo specificato.
- Nessun argomento. Se non vengono inviati argomenti, la lunghezza dell'elenco
paramsè zero.
Nell'esempio seguente vengono illustrati vari modi per inviare argomenti a un params parametro.
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 risoluzione dell'overload può causare ambiguità quando l'argomento per un params parametro è un tipo di raccolta. Il tipo di raccolta dell'argomento deve essere convertibile nel tipo di raccolta del parametro . Quando overload diversi forniscono conversioni migliori per tale parametro, questo metodo potrebbe essere migliore. Tuttavia, se l'argomento del params parametro è discreto o mancante, tutti gli overload con tipi di parametro diversi params sono uguali per tale parametro.
Per altre informazioni, vedere la sezione relativa agli elenchi di argomenti nella specifica del linguaggio C#. La specifica del linguaggio costituisce il riferimento ufficiale principale per la sintassi e l'uso di C#.