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.
Gli scarti sono variabili segnaposto intenzionalmente inutilizzate nel codice dell'applicazione. Scarti sono equivalenti alle variabili non assegnate; non hanno un valore. Un discard comunica l'intento al compilatore e ad altri che leggono il tuo codice: intendevi ignorare il risultato di un'espressione. È possibile ignorare il risultato di un'espressione, uno o più membri di una tupla, un out
parametro di un metodo o il target di un'espressione di corrispondenza di modelli.
Scarti rendono chiara l'intenzione del tuo codice. Un simbolo discard indica che il nostro codice non utilizza mai la variabile. Migliorano la leggibilità e la manutenibilità.
Si indica una variabile come scarto assegnandole il carattere di sottolineatura (_
) come nome. Ad esempio, la chiamata al metodo seguente restituisce una tupla in cui i primi e i secondi valori vengono eliminati.
area
è una variabile dichiarata in precedenza impostata sul terzo componente restituito da GetCityInformation
:
(_, _, area) = city.GetCityInformation(cityName);
È possibile usare le eliminazioni per specificare i parametri di input inutilizzati di un'espressione lambda. Per altre informazioni, vedere la sezione Parametri di input di un'espressione lambda dell'articolo Espressioni lambda .
Quando _
è un'operazione di rimozione valida, il tentativo di recuperarne il valore o usarlo in un'operazione di assegnazione genera l'errore del compilatore CS0103, "Il nome '_' non esiste nel contesto corrente". Questo errore è dovuto al fatto che _
non viene assegnato un valore e potrebbe non essere assegnato nemmeno a un percorso di archiviazione. Se si trattasse di una variabile effettiva, non è stato possibile rimuovere più di un valore, come nell'esempio precedente.
Decostruzione di tuple e oggetti
Gli scarti sono utili per lavorare con le tuple quando il codice dell'applicazione utilizza alcuni elementi della tupla, ma ne ignora altri. Ad esempio, il metodo seguente QueryCityDataForYears
restituisce una tupla con il nome di una città, la relativa area, un anno, la popolazione della città per quell'anno, un secondo anno e la popolazione della città per il secondo anno. L'esempio visualizza la variazione della popolazione tra questi due anni. Tra i dati resi disponibili dalla tupla non interessa l'area della città, mentre il nome della città e le due date sono già noti in fase di progettazione. Di conseguenza interessano soltanto i due valori di popolazione archiviati nella tupla, mentre gli altri valori possono essere gestiti come variabili discard.
var (_, _, _, pop1, _, pop2) = QueryCityDataForYears("New York City", 1960, 2010);
Console.WriteLine($"Population change, 1960 to 2010: {pop2 - pop1:N0}");
static (string, double, int, int, int, int) QueryCityDataForYears(string name, int year1, int year2)
{
int population1 = 0, population2 = 0;
double area = 0;
if (name == "New York City")
{
area = 468.48;
if (year1 == 1960)
{
population1 = 7781984;
}
if (year2 == 2010)
{
population2 = 8175133;
}
return (name, area, year1, population1, year2, population2);
}
return ("", 0, 0, 0, 0, 0);
}
// The example displays the following output:
// Population change, 1960 to 2010: 393,149
Per ulteriori informazioni sulla decostruzione delle tuple con discards, vedere Deconstructing tuples and other types.
Il Deconstruct
metodo di una classe, una struttura o un'interfaccia consente anche di recuperare e decostruire un set specifico di dati da un oggetto . È possibile usare le eliminazioni quando si è interessati a usare solo un subset dei valori deconstruiti. Nell'esempio seguente viene decomposto un Person
oggetto in quattro stringhe (il nome e il cognome, la città e lo stato), ma ignora il cognome e lo stato.
using System;
namespace Discards
{
public class Person
{
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
public string City { get; set; }
public string State { get; set; }
public Person(string fname, string mname, string lname,
string cityName, string stateName)
{
FirstName = fname;
MiddleName = mname;
LastName = lname;
City = cityName;
State = stateName;
}
// Return the first and last name.
public void Deconstruct(out string fname, out string lname)
{
fname = FirstName;
lname = LastName;
}
public void Deconstruct(out string fname, out string mname, out string lname)
{
fname = FirstName;
mname = MiddleName;
lname = LastName;
}
public void Deconstruct(out string fname, out string lname,
out string city, out string state)
{
fname = FirstName;
lname = LastName;
city = City;
state = State;
}
}
class Example
{
public static void Main()
{
var p = new Person("John", "Quincy", "Adams", "Boston", "MA");
// Deconstruct the person object.
var (fName, _, city, _) = p;
Console.WriteLine($"Hello {fName} of {city}!");
// The example displays the following output:
// Hello John of Boston!
}
}
}
Per ulteriori informazioni sulla decostruzione dei tipi definiti dall'utente con scarti, vedere Decostruzione di tuple e altri tipi.
Corrispondenza di modelli con switch
Il modello di scarto può essere usato nel pattern matching con l'espressione switch. Ogni espressione, inclusa null
, corrisponde sempre al modello di scarto.
Nell'esempio seguente viene definito un ProvidesFormatInfo
metodo che usa un'espressione switch
per determinare se un oggetto fornisce un'implementazione IFormatProvider e verifica se l'oggetto è null
. Usa anche il modello discard per gestire oggetti non Null di qualsiasi altro tipo.
object?[] objects = [CultureInfo.CurrentCulture,
CultureInfo.CurrentCulture.DateTimeFormat,
CultureInfo.CurrentCulture.NumberFormat,
new ArgumentException(), null];
foreach (var obj in objects)
ProvidesFormatInfo(obj);
static void ProvidesFormatInfo(object? obj) =>
Console.WriteLine(obj switch
{
IFormatProvider fmt => $"{fmt.GetType()} object",
null => "A null object reference: Its use could result in a NullReferenceException",
_ => "Some object type without format information"
});
// The example displays the following output:
// System.Globalization.CultureInfo object
// System.Globalization.DateTimeFormatInfo object
// System.Globalization.NumberFormatInfo object
// Some object type without format information
// A null object reference: Its use could result in a NullReferenceException
Chiamate ai metodi con out
parametri
Quando si chiama il Deconstruct
metodo per decostruire un tipo definito dall'utente (un'istanza di una classe, una struttura o un'interfaccia), è possibile rimuovere i valori dei singoli out
argomenti. Tuttavia, è anche possibile eliminare il valore degli out
argomenti quando si chiama qualsiasi metodo con un out
parametro .
Nell'esempio seguente si chiama il metodo DateTime.TryParse(String, out DateTime) per determinare se la rappresentazione di stringa di una data è valida nella cultura attuale. Dato che l'esempio riguarda solo la convalida della stringa di data e non l'analisi per estrarre la data, l'argomento out
per il metodo è un valore eliminato.
string[] dateStrings = ["05/01/2018 14:57:32.8", "2018-05-01 14:57:32.8",
"2018-05-01T14:57:32.8375298-04:00", "5/01/2018",
"5/01/2018 14:57:32.80 -07:00",
"1 May 2018 2:57:32.8 PM", "16-05-2018 1:00:32 PM",
"Fri, 15 May 2018 20:10:57 GMT"];
foreach (string dateString in dateStrings)
{
if (DateTime.TryParse(dateString, out _))
Console.WriteLine($"'{dateString}': valid");
else
Console.WriteLine($"'{dateString}': invalid");
}
// The example displays output like the following:
// '05/01/2018 14:57:32.8': valid
// '2018-05-01 14:57:32.8': valid
// '2018-05-01T14:57:32.8375298-04:00': valid
// '5/01/2018': valid
// '5/01/2018 14:57:32.80 -07:00': valid
// '1 May 2018 2:57:32.8 PM': valid
// '16-05-2018 1:00:32 PM': invalid
// 'Fri, 15 May 2018 20:10:57 GMT': invalid
Un'eliminazione indipendente
È possibile usare un'eliminazione autonoma per indicare qualsiasi variabile che si sceglie di ignorare. Un uso tipico consiste nell'usare un'assegnazione per assicurarsi che un argomento non sia Null. Il codice seguente usa un'istruzione discard per forzare un'assegnazione. Il lato destro dell'assegnazione usa l'operatore di unione Null per generare un System.ArgumentNullException oggetto quando l'argomento è null
. Il codice non richiede il risultato dell'assegnazione, quindi viene rimosso. L'espressione forza un controllo Null. L'eliminazione chiarisce il tuo intento: il risultato dell'assegnazione non è né necessario né utilizzato.
public static void Method(string arg)
{
_ = arg ?? throw new ArgumentNullException(paramName: nameof(arg), message: "arg can't be null");
// Do work with arg.
}
Nell'esempio seguente viene utilizzata una rimozione autonoma per ignorare l'oggetto Task restituito da un'operazione asincrona. L'assegnazione dell'attività ha l'effetto di sopprimere l'eccezione generata dall'operazione quando sta per essere completata. Rende chiara la finalità: si vuole eliminare l'oggetto Task
e ignorare eventuali errori generati da tale operazione asincrona.
private static async Task ExecuteAsyncMethods()
{
Console.WriteLine("About to launch a task...");
_ = Task.Run(() =>
{
var iterations = 0;
for (int ctr = 0; ctr < int.MaxValue; ctr++)
iterations++;
Console.WriteLine("Completed looping operation...");
throw new InvalidOperationException();
});
await Task.Delay(5000);
Console.WriteLine("Exiting after 5 second delay");
}
// The example displays output like the following:
// About to launch a task...
// Completed looping operation...
// Exiting after 5 second delay
Senza assegnare l'attività a un'operazione di eliminazione, il codice seguente genera un avviso del compilatore:
private static async Task ExecuteAsyncMethods()
{
Console.WriteLine("About to launch a task...");
// CS4014: Because this call is not awaited, execution of the current method continues before the call is completed.
// Consider applying the 'await' operator to the result of the call.
Task.Run(() =>
{
var iterations = 0;
for (int ctr = 0; ctr < int.MaxValue; ctr++)
iterations++;
Console.WriteLine("Completed looping operation...");
throw new InvalidOperationException();
});
await Task.Delay(5000);
Console.WriteLine("Exiting after 5 second delay");
Annotazioni
Se si esegue uno dei due esempi precedenti usando un debugger, il debugger arresterà il programma quando viene generata l'eccezione. Senza un debugger collegato, l'eccezione viene ignorata automaticamente in entrambi i casi.
_
è anche un identificatore valido. Se usato all'esterno di un contesto supportato, _
viene considerato non come una variabile scartata, ma come variabile valida. Se un identificatore denominato _
è già in ambito, l'uso di _
come variabile di scarto autonoma può comportare:
- Modifica accidentale del valore della variabile nell'ambito
_
assegnandole il valore dell'eliminazione prevista. Per esempio:private static void ShowValue(int _) { byte[] arr = [0, 0, 1, 2]; _ = BitConverter.ToInt32(arr, 0); Console.WriteLine(_); } // The example displays the following output: // 33619968
- Errore del compilatore per violare la sicurezza dei tipi. Per esempio:
private static bool RoundTrips(int _) { string value = _.ToString(); int newValue = 0; _ = Int32.TryParse(value, out newValue); return _ == newValue; } // The example displays the following compiler error: // error CS0029: Cannot implicitly convert type 'bool' to 'int'