Dela via


Ignorera – grunderna i C#

Borttagna är platshållarvariabler som avsiktligt används i programkoden. Borttagningar motsvarar otilldelade variabler. de har inget värde. Ett ignorerande kommunicerar avsikten med kompilatorn och andra som läser koden: Du har för avsikt att ignorera resultatet av ett uttryck. Du kanske vill ignorera resultatet av ett uttryck, en eller flera medlemmar i ett tupppeluttryck, en out parameter till en metod eller målet för ett mönstermatchningsuttryck.

Ignorera gör avsikten med koden tydlig. Ett ignorerande anger att vår kod aldrig använder variabeln. De förbättrar dess läsbarhet och underhållbarhet.

Du anger att en variabel är ignorerad genom att tilldela den understrecket (_) som dess namn. Följande metodanrop returnerar till exempel en tupplar där de första och andra värdena ignoreras. area är en tidigare deklarerad variabel inställd på den tredje komponenten som returneras av GetCityInformation:

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

Du kan använda ignorera för att ange oanvända indataparametrar för ett lambda-uttryck. Mer information finns i avsnittet Indataparametrar för ett lambda-uttryck i artikeln Lambda-uttryck .

När _ är ett giltigt ignorerande genererar försök att hämta dess värde eller använda det i en tilldelningsåtgärd kompilatorfel CS0103, "Namnet '_' finns inte i den aktuella kontexten". Det här felet beror på att _ inte har tilldelats något värde och kanske inte ens har tilldelats en lagringsplats. Om det var en faktisk variabel kunde du inte ta bort fler än ett värde, som i föregående exempel.

Tuppeln och objektdekonstruktionen

Borttagningar är användbara när du arbetar med tupplar när programkoden använder vissa tupplar men ignorerar andra. Följande QueryCityDataForYears metod returnerar till exempel en tuppeln med namnet på en stad, dess område, ett år, stadens befolkning för det året, ett andra år och stadens befolkning för det andra året. Exemplet visar befolkningsförändringen mellan dessa två år. Av de data som är tillgängliga från tuppeln är vi obekymrade med stadsområdet, och vi känner till stadens namn och de två datumen vid designtiden. Därför är vi bara intresserade av de två populationsvärden som lagras i tuppeln och kan hantera dess återstående värden som borttagna.

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

Mer information om hur du dekonstruerar tupplar med borttagningar finns i Dekonstruera tupplar och andra typer.

Med Deconstruct metoden för en klass, struktur eller ett gränssnitt kan du också hämta och dekonstruera en specifik uppsättning data från ett objekt. Du kan använda ignorera när du är intresserad av att bara arbeta med en delmängd av de dekonstruerade värdena. I följande exempel dekonstrueras ett Person objekt i fyra strängar (för- och efternamnen, staden och staten), men efternamnet och tillståndet ignoreras.

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

Mer information om hur du dekonstruerar användardefinierade typer med borttagningar finns i Dekonstruera tupplar och andra typer.

Mönstermatchning med switch

Mönstret ignorera kan användas i mönstermatchning med switch-uttrycket. Varje uttryck, inklusive null, matchar alltid mönstret ignorera.

I följande exempel definieras en ProvidesFormatInfo metod som använder ett switch uttryck för att avgöra om ett objekt tillhandahåller en IFormatProvider implementering och testar om objektet är null. Den använder också mönstret ignorera för att hantera icke-null-objekt av någon annan typ.

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

Anrop till metoder med out parametrar

När du anropar Deconstruct metoden för att dekonstruera en användardefinierad typ (en instans av en klass, struktur eller ett gränssnitt) kan du ta bort värdena för enskilda out argument. Men du kan också ta bort värdet för out argument när du anropar valfri metod med en out parameter.

I följande exempel anropas metoden DateTime.TryParse(String, out DateTime) för att avgöra om strängrepresentationen av ett datum är giltig i den aktuella kulturen. Eftersom exemplet endast handlar om att verifiera datumsträngen och inte parsa den för att extrahera datumet, out är argumentet till metoden ett ignorerande.

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

En fristående ignorera

Du kan använda en fristående ignorera för att ange vilken variabel som helst som du väljer att ignorera. En vanlig användning är att använda en tilldelning för att säkerställa att ett argument inte är null. Följande kod använder en ignorera för att framtvinga en tilldelning. Till höger i tilldelningen används operatorn null coalescing för att utlösa en System.ArgumentNullException när argumentet är null. Koden behöver inte resultatet av tilldelningen, så den tas bort. Uttrycket tvingar fram en null-kontroll. Ignorera klargör din avsikt: resultatet av tilldelningen behövs inte eller används inte.

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

    // Do work with arg.
}

I följande exempel används en fristående ignorera för att ignorera objektet Task som returneras av en asynkron åtgärd. Att tilldela uppgiften innebär att undantaget som åtgärden genererar undertrycks när den är på väg att slutföras. Det gör avsikten Tasktydlig: Du vill ignorera , och ignorera eventuella fel som genereras från den asynkrona åtgärden.

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

Utan att tilldela uppgiften till en ignorerad kod genererar följande kod en kompilatorvarning:

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

Kommentar

Om du kör något av de föregående två exemplen med hjälp av ett felsökningsprogram stoppar felsökningsprogrammet programmet när undantaget utlöses. Utan att ett felsökningsprogram är kopplat ignoreras undantaget tyst i båda fallen.

_ är också en giltig identifierare. När den används utanför en kontext _ som stöds behandlas den inte som en ignorerad utan som en giltig variabel. Om en identifierare med namnet _ redan finns i omfånget kan användningen av _ som en fristående ignorerande resultera i:

  • Oavsiktlig ändring av värdet för variabeln i omfånget _ genom att tilldela den värdet för den avsedda ignorera. Till exempel:
    private static void ShowValue(int _)
    {
       byte[] arr = [0, 0, 1, 2];
       _ = BitConverter.ToInt32(arr, 0);
       Console.WriteLine(_);
    }
     // The example displays the following output:
     //       33619968
    
  • Ett kompilatorfel för brott mot typsäkerhet. Till exempel:
    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'
    
  • Kompilatorfel CS0136: "Det går inte att deklarera en lokal parameter eller parameter med namnet '_' i det här omfånget eftersom det namnet används i ett omslutande lokalt omfång för att definiera en lokal eller parameter." Till exempel:
     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
    

Se även