Sdílet prostřednictvím


Zahození – Základy jazyka C#

Zahození jsou zástupné proměnné, které jsou záměrně nepoužívané v kódu aplikace. Zahození se rovná nepřiřazeným proměnným; nemají hodnotu. Zahození oznamuje záměr kompilátoru a dalším uživatelům, kteří čtou váš kód: Chtěli jste ignorovat výsledek výrazu. Můžete chtít ignorovat výsledek výrazu, jednoho nebo více členů výrazu řazené kolekce členů, out parametr metody nebo cíl vzorového odpovídajícího výrazu.

Zahodí záměr vašeho kódu. Zahození znamená, že náš kód proměnnou nikdy nepoužívá. Vylepšují jeho čitelnost a udržovatelnost.

Označujete, že proměnná je zahozená tím, že ji přiřadíte podtržítko (_) jako její název. Například následující volání metody vrátí řazenou kolekci členů, ve které jsou první a druhé hodnoty zahozeny. area je dříve deklarovaná proměnná nastavená na třetí komponentu vrácenou GetCityInformation:

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

Pomocí zahození můžete zadat nepoužité vstupní parametry výrazu lambda. Další informace najdete v části Vstupní parametry výrazulambda článku Výrazy lambda.

Pokud _ je platná zahození, pokus o načtení jeho hodnoty nebo jeho použití v operaci přiřazení vygeneruje chybu kompilátoru CS0103, "Název '_' neexistuje v aktuálním kontextu". Tato chyba je způsobená _ tím, že není přiřazena hodnota a nemusí být ani přiřazena umístění úložiště. Pokud se jednalo o skutečnou proměnnou, nepodařilo se vám zahodit více než jednu hodnotu, jak tomu bylo v předchozím příkladu.

Dekonstrukce řazené kolekce členů a objektu

Zahození je užitečné při práci s řazenými kolekcemi členů, když kód aplikace používá některé prvky řazené kolekce členů, ale ignoruje ostatní. Například následující QueryCityDataForYears metoda vrátí řazenou kolekci členů s názvem města, jeho oblastí, rokem, počtem obyvatel města pro daný rok, druhým rokem a počtem obyvatel města pro tento druhý rok. Příklad ukazuje změnu populace mezi těmito dvěma roky. Z dat dostupných z řazené kolekce členů jsme nespokojení s oblastí města a známe název města a dvě data v době návrhu. V důsledku toho nás zajímají pouze dvě hodnoty základního souboru uložené v řazené kolekci členů a můžeme zpracovat zbývající hodnoty jako zahozené.

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

Další informace o dekonstrukci řazených kolekcí členů pomocí zahození naleznete v tématu Deconstructing řazené kolekce členů a další typy.

Metoda Deconstruct třídy, struktury nebo rozhraní také umožňuje načíst a dekonstruovat konkrétní sadu dat z objektu. Pokud chcete pracovat jenom s podmnožinou dekonstruovaných hodnot, můžete použít zahození. Následující příklad dekonstruuje Person objekt do čtyř řetězců (křestní jméno a příjmení, město a stát), ale zahodí příjmení a stát.

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

Další informace o dekonstrukci uživatelem definovaných typů pomocí zahození naleznete v tématu Dekonstrukce řazených kolekcí členů a dalších typů.

Porovnávání vzorů s switch

Vzor zahození lze použít ve vzorovém porovnávání s výrazem switch. Každý výraz, včetně null, vždy odpovídá vzoru zahození.

Následující příklad definuje metodu ProvidesFormatInfo , která používá switch výraz k určení, zda objekt poskytuje IFormatProvider implementaci a testuje, zda objekt je null. Používá také vzor zahození ke zpracování objektů, které nejsou null jakéhokoli jiného typu.

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

Volání metod s out parametry

Při volání Deconstruct metody dekonstruovat uživatelem definovaný typ (instance třídy, struktury nebo rozhraní), můžete zahodit hodnoty jednotlivých out argumentů. Můžete ale také zahodit hodnotu argumentů out při volání jakékoli metody s parametrem out .

Následující příklad volá Metodu DateTime.TryParse(String, out DateTime), která určuje, zda je řetězcové vyjádření data platné v aktuální jazykové verzi. Vzhledem k tomu, že příklad se zabývá pouze ověřením řetězce kalendářního data a nikoli analýzou data, out argument metody je zahozen.

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

Samostatná zahození

Pomocí samostatného zahození můžete označit libovolnou proměnnou, kterou chcete ignorovat. Jedním z typických použití je použití přiřazení k zajištění, že argument není null. Následující kód používá k vynucení přiřazení zahození. Pravá strana přiřazení používá operátor nulového sjednocení k vyvolání System.ArgumentNullException , když je nullargument . Kód nepotřebuje výsledek přiřazení, takže se zahodí. Výraz vynutí kontrolu null. Zahození upřesňuje váš záměr: výsledek přiřazení není potřeba ani použit.

public static void Method(string arg)
{
    _ = arg ?? throw new ArgumentNullException(paramName: nameof(arg), message: "arg can't be null");

    // Do work with arg.
}

Následující příklad používá samostatnou zahození k ignorování objektu Task vráceného asynchronní operací. Přiřazení úlohy má vliv na potlačení výjimky, kterou operace vyvolá, protože se chystá dokončit. Váš záměr je jasný: Chcete zahodit Taska ignorovat všechny chyby vygenerované z této asynchronní operace.

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

Bez přiřazení úlohy k zahození vygeneruje následující kód upozornění kompilátoru:

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");

Poznámka:

Pokud spustíte některou z předchozích dvou ukázek pomocí ladicího programu, ladicí program program zastaví při vyvolání výjimky. Bez připojeného ladicího programu se výjimka v obou případech bezobslužně ignoruje.

_ je také platný identifikátor. Při použití mimo podporovaný kontext se považuje za zahození, _ ale jako platnou proměnnou. Pokud je název _ identifikátoru již v oboru, může použití _ jako samostatného zahození vést k:

  • Náhodné úpravy hodnoty proměnné v oboru _ tím, že jí přiřadíte hodnotu zamýšleného zahození. Příklad:
    private static void ShowValue(int _)
    {
       byte[] arr = [0, 0, 1, 2];
       _ = BitConverter.ToInt32(arr, 0);
       Console.WriteLine(_);
    }
     // The example displays the following output:
     //       33619968
    
  • Chyba kompilátoru pro porušení bezpečnosti typů. Příklad:
    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'
    
  • Chyba kompilátoru CS0136: Místní nebo parametr s názvem _nelze v tomto oboru deklarovat, protože tento název se používá v nadřazeném místním oboru k definování místního nebo parametru. Příklad:
     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
    

Viz také