Variabili discard - Guida di C#Discards - C# Guide

A partire dalla versione 7.0, C# supporta le variabili discard, variabili dummy temporanee intenzionalmente inutilizzate nel codice dell'applicazione.Starting with C# 7.0, C# supports discards, which are temporary, dummy variables that are intentionally unused in application code. Le variabili discard sono equivalenti alle variabili non assegnate e non hanno un valore.Discards are equivalent to unassigned variables; they do not have a value. Dato che è presente un'unica variabile discard alla quale potrebbe non essere allocata nessuna archiviazione, le variabili discard possono ridurre le allocazioni di memoria.Because there is only a single discard variable, and that variable may not even be allocated storage, discards can reduce memory allocations. Dato che rendono chiaro l'obiettivo del codice, ne migliorano la leggibilità e la gestibilità.Because they make the intent of your code clear, they enhance its readability and maintainability.

Per indicare che una variabile è una variabile discard le si assegna come nome il carattere di sottolineatura (_).You indicate that a variable is a discard by assigning it the underscore (_) as its name. Ad esempio, la chiamata al metodo seguente restituisce una tupla con 3 elementi in cui il primo e il secondo valore sono discard e area è una variabile dichiarata in precedenza da impostare in base al corrispondente terzo componente restituito da GetCityInformation:For example, the following method call returns a 3-tuple in which the first and second values are discards and area is a previously declared variable to be set to the corresponding third component returned by GetCityInformation:

(_, _, area) = city.GetCityInformation(cityName);

In C# 7.0 le variabili discard sono supportate nelle assegnazioni nei contesti seguenti:In C# 7.0, discards are supported in assignments in the following contexts:

  • Decostruzione di tuple e oggetti.Tuple and object deconstruction.
  • Criteri di ricerca con is e switch.Pattern matching with is and switch.
  • Chiamate a metodi con parametri out.Calls to methods with out parameters.
  • Un _ standalone quando l'ambito non include nessun _.A standalone _ when no _ is in scope.

Quando _ è una variabile discard valida, se si prova a recuperarne il valore o a usarla in un'operazione di assegnazione viene generato l'errore di compilazione CS0301, "Il nome '_' non esiste nel contesto corrente".When _ is a valid discard, attempting to retrieve its value or use it in an assignment operation generates compiler error CS0301, "The name '_' does not exist in the current context". L'errore si verifica perché a _ non è assegnato nessun valore e potrebbe non essere assegnata nessuna posizione di archiviazione.This is because _ is not assigned a value, and may not even be assigned a storage location. Se si trattasse di una vera variabile non sarebbe possibile rimuovere più di un valore, come nell'esempio precedente.If it were an actual variable, you could not discard more than one value, as the previous example did.

Decostruzione di tuple e oggettiTuple and object deconstruction

Le variabili discard sono particolarmente utili quando si lavora con le tuple e il codice dell'applicazione usa alcuni elementi di una tupla ma ne ignora altri.Discards are particularly useful in working with tuples when your application code uses some tuple elements but ignores others. Ad esempio il metodo QueryCityDataForYears seguente restituisce una tupla con 6 elementi con il nome di una città, l'area della città, un anno, la popolazione della città per tale anno, un secondo anno e la popolazione della città per tale anno.For example, the following QueryCityDataForYears method returns a 6-tuple with the name of a city, its area, a year, the city's population for that year, a second year, and the city's population for that second year. L'esempio visualizza la variazione della popolazione tra questi due anni.The example shows the change in population between those two years. 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.Of the data available from the tuple, we're unconcerned with the city area, and we know the city name and the two dates at design-time. Di conseguenza interessano soltanto i due valori di popolazione archiviati nella tupla, mentre gli altri valori possono essere gestiti come variabili discard.As a result, we're only interested in the two population values stored in the tuple, and can handle its remaining values as discards.

using System;
using System.Collections.Generic;

public class Example
{
   public static void Main()
   {
       var (_, _, _, pop1, _, pop2) = QueryCityDataForYears("New York City", 1960, 2010);

       Console.WriteLine($"Population change, 1960 to 2010: {pop2 - pop1:N0}");
   }
   
   private 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 altre informazioni sulla decostruzione di tuple con le variabili discard, vedere Decostruzione di tuple e altri tipi.For more information on deconstructing tuples with discards, see Deconstructing tuples and other types.

Anche il metodo Deconstruct di una classe, struttura o interfaccia consente di recuperare e decostruire un set di dati specifico da un oggetto.The Deconstruct method of a class, structure, or interface also allows you to retrieve and deconstruct a specific set of data from an object. È possibile usare le variabili quando risulta utile lavorare solo con un subset dei valori decostruiti.You can use discards when you are interested in working with only a subset of the deconstructed values. L'esempio seguente esegue la decostruzione di un oggetto Person in quattro stringhe (nome, cognome, città e stato), ma rimuove il cognome e lo stato.The following example deconstructs a Person object into four strings (the first and last names, the city, and the state), but discards the last name and the state.

using System;

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;
   }                           
}

public class Example
{
   public static void Main()
   {
       Person p = new Person("John", "Quincy", "Adams", "Boston", "MA");

       // <Snippet1>
       // Deconstruct the person object.
       var (fName, _, city, _) = p;
       Console.WriteLine($"Hello {fName} of {city}!");
       // The example displays the following output:
       //      Hello John of Boston!       
       // </Snippet1>
   }
}
// The example displays the following output:
//    Hello John Adams of Boston, MA!

Per altre informazioni sulla decostruzione di tipi definiti dall'utente con le variabili discard, vedere Decostruzione di tuple e altri tipi.For more information on deconstructing user-defined types with discards, see Deconstructing tuples and other types.

Criteri di ricerca con switch e isPattern matching with switch and is

Il criterio variabile discard può essere usato nei criteri di ricerca con le parole chiave is e switch.The discard pattern can be used in pattern matching with the is and switch keywords. Ogni espressione corrisponde sempre al criterio variabile discard.Every expression always matches the discard pattern.

L'esempio seguente definisce un metodo ProvidesFormatInfo che usa istruzioni is per determinare se un oggetto include un'implementazione IFormatProvider e verifica se l'oggetto null.The following example defines a ProvidesFormatInfo method that uses is statements to determine whether an object provides an IFormatProvider implementation and tests whether the object is null. Usa anche il criterio variabile discard per gestire gli oggetti non null di qualsiasi altro tipo.It also uses the discard pattern to handle non-null objects of any other type.

using System;
using System.Globalization;

public class Example
{
   public static void Main()
   {
      object[] objects = { CultureInfo.CurrentCulture, 
                           CultureInfo.CurrentCulture.DateTimeFormat, 
                           CultureInfo.CurrentCulture.NumberFormat,
                           new ArgumentException(), null };
      foreach (var obj in objects)
         ProvidesFormatInfo(obj);
   }

   private static void ProvidesFormatInfo(object obj)         
   {
      if (obj is IFormatProvider fmt)
         Console.WriteLine($"{fmt} object");
      else if (obj is null) {
         Console.Write("A null object reference: ");
         Console.WriteLine("Its use could result in a NullReferenceException");
      }
      else
         Console.WriteLine($"Some object type without format information");
   }
}
// The example displays the following output:
//    en-US 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 a metodi con parametri outCalls to methods with out parameters

Quando si chiama il metodo Deconstruct per la decostruzione di un tipo definito dall'utente (un'istanza di una classe, una struttura o un'interfaccia), è possibile rimuovere i valori di singoli argomenti out.When calling the Deconstruct method to deconstruct a user-defined type (an instance of a class, structure, or interface), you can discard the values of individual out arguments. Tuttavia è possibile rimuovere il valore degli argomenti out anche quando si chiama qualsiasi metodo con un parametro out.But you can also discard the value of out arguments when calling any method with an out parameter.

Nel seguente esempio viene chiamato il metodo DateTime.TryParse(String, out DateTime) per determinare se la rappresentazione stringa di una data è valida con le impostazioni cultura correnti.The following example calls the DateTime.TryParse(String, out DateTime) method to determine whether the string representation of a date is valid in the current culture. Dato che lo scopo dell'esempio è solo quello di convalidare la stringa di data e non quello di analizzarla per estrarre la data, l'argomento out del metodo è una variabile discard.Because the example is concerned only with validating the date string and not with parsing it to extract the date, the out argument to the method is a discard.

using System;

public class Example
{
   public static void Main()
   {
      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

Una variabile discard standaloneA standalone discard

È possibile usare una variabile discard standalone per indicare qualsiasi variabile che si è deciso di ignorare.You can use a standalone discard to indicate any variable that you choose to ignore. Nell'esempio seguente viene usata una variabile discard standalone per ignorare l'oggetto Task restituito da un'operazione asincrona.The following example uses a standalone discard to ignore the Task object returned by an asynchronous operation. Di conseguenza viene eliminata l'eccezione che viene attivata dall'operazione poco prima del completamento.This has the effect of suppressing the exception that the operation throws as it is about to complete.

using System;
using System.Threading.Tasks;

public class Example
{
   public static void Main()
   {
      ExecuteAsyncMethods().Wait();
   }

   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

Si noti che anche _ è un identificatore valido.Note that _ is also a valid identifier. Quando viene usata fuori da un contesto supportato, _ non viene considerata come una variabile discard ma come una variabile valida.When used outside of a supported context, _ is treated not as a discard but as a valid variable. Se un identificatore con nome _ è già incluso nell'ambito, l'uso di _ come variabile discard standalone può causare:If an identifier named _ is already in scope, the use of _ as a standalone discard can result in:

  • La modifica accidentale della variabile _ dell'ambito, alla quale viene assegnato il valore della variabile discard prevista.Accidental modification of the value of the in-scope _ variable by assigning it the value of the intended discard. Ad esempio:For example:

    private static void ShowValue(int _)
    {
       byte[] arr = { 0, 0, 1, 2 };
       _ = BitConverter.ToInt32(arr, 0);
       Console.WriteLine(_);
    }
    // The example displays the following output:
    //       33619968
    
  • Un errore del compilatore per la violazione dell'indipendenza dai tipi.A compiler error for violating type safety. Ad esempio:For example:

    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'   
    
  • Errore del compilatore CS0136: "Non è possibile dichiarare in questo ambito una variabile locale o un parametro denominato "_" perché tale nome viene usato in un ambito locale di inclusione per definire una variabile locale o un parametro".Compiler error CS0136, "A local or parameter named '_' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter." Ad esempio:For example:

    public void DoSomething(int _) 
    {
     var _ = GetValue(); // Error: cannot declare local _ when one is already in scope
    }   
    // The example displays the following compiler error:
    // error CS0136: 
    //       A local or parameter named '_' cannot be declared in this scope 
    //       because that name is used in an enclosing local scope 
    //       to define a local or parameter   
    

Vedere ancheSee also