Share via


Elvetések – A C# alapjai

Az elvetések olyan helyőrző változók, amelyek szándékosan nincsenek használatban az alkalmazáskódban. Az elvetések egyenértékűek a nem hozzárendelt változókkal; nincs értékük. Az elvetés szándékot közöl a fordítóval és a kódot olvasó többi felhasználóval: Ön egy kifejezés eredményét figyelmen kívül szeretné hagyni. Előfordulhat, hogy figyelmen kívül szeretné hagyni egy kifejezés eredményét, egy vagy több tagot, egy metódus paraméterét out vagy egy mintamegfeleltetési kifejezés célját.

Az elvetések egyértelművé teszik a kód szándékát. Az elvetés azt jelzi, hogy a kód soha nem használja a változót. Javítják az olvashatóságot és a karbantarthatóságot.

Azt jelzi, hogy egy változó elvetés, ha az aláhúzásjelet (_) rendeli hozzá a nevéhez. A következő metódushívás például egy rekordot ad vissza, amelyben az első és a második érték elvetve lesz. area egy korábban deklarált változó, amely a következő által visszaadott GetCityInformationharmadik összetevőre van állítva:

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

A lambdakifejezések nem használt bemeneti paramétereit elvetések használatával adhatja meg. További információkért tekintse meg a Lambda-kifejezések cikk lambdakifejezés-szakaszának bemeneti paramétereit.

Érvényes elvetés esetén _ , ha megkísérli lekérni az értékét, vagy egy hozzárendelési műveletben használni, cs0103-as fordítási hibát generál, "A_név nem létezik az aktuális környezetben". Ez a hiba azért van, mert _ nincs hozzárendelve érték, és lehet, hogy még tárolóhelyet sem rendel hozzá. Ha tényleges változóról van szó, nem lehet több értéket elvetni, mint az előző példában.

Tuple és object deconstruction

Az elvetések akkor hasznosak, ha az alkalmazáskód néhány rekordelemet használ, de figyelmen kívül hagy másokat. Az alábbi QueryCityDataForYears módszer például egy város, annak területe, egy év, az adott év lakossága, egy második év és a város második évre vonatkozó népességét adja vissza. A példa a két év közötti népességváltozást mutatja be. A rekordból elérhető adatok közül a városterülettel nem vagyunk tisztában, és tudjuk a város nevét és a két dátumot a tervezéskor. Ennek eredményeképpen csak a rekordban tárolt két sokaságérték érdekel minket, és a fennmaradó értékeket elvetésként tudja kezelni.

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

A csonkok elvetéssel történő dekonstruálásával kapcsolatos további információkért lásd a csonkok és más típusok dekonstruálását ismertető témakört.

Az Deconstruct osztály, a struktúra vagy az interfész metódusa lehetővé teszi egy adott adatkészlet lekérését és dekonstruálását is egy objektumból. Elvetéseket akkor használhat, ha csak a dekonstruált értékek egy részhalmazával szeretne dolgozni. Az alábbi példa egy objektumot négy sztringre Person (az utó- és vezetéknevekre, a városra és az államra) bont, de elveti a vezetéknevet és az államot.

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

A felhasználó által definiált típusok elvetéssel történő dekonstruálásával kapcsolatos további információkért lásd : Deconstructing tuples and other types.

Mintaegyezés a switch

Az elvetési minta a kapcsolókifejezéssel egyező mintában használható. Minden kifejezés, beleértve az nullelvetési mintát is, mindig egyezik.

Az alábbi példa egy metódust ProvidesFormatInfo határoz meg, amely kifejezéssel switch határozza meg, hogy egy objektum implementációt IFormatProvider biztosít-e, és ellenőrzi, hogy az objektum valóban az-e null. Emellett az elvetési mintát használja bármely más típusú nem null értékű objektumok kezelésére.

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

Metódusok meghívása paraméterekkel out

Ha meghívja a Deconstruct felhasználó által definiált típus (osztály, struktúra vagy felület egy példánya) dekonstruálásához a metódust, elvetheti az egyes out argumentumok értékeit. Az argumentumok értékét out azonban elvetheti, ha bármely metódust out paraméterrel hív meg.

Az alábbi példa meghívja a DateTime.TryParse(Sztring, out DateTime) metódust annak megállapításához, hogy egy dátum sztring-ábrázolása érvényes-e az aktuális kultúrában. Mivel a példa csak a dátumsztring érvényesítésével foglalkozik, és nem a dátum kinyeréséhez szükséges elemzéssel, a out metódus argumentuma elvetés.

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

Önálló elvetés

Önálló elvetés használatával jelezheti azokat a változót, amelyeket figyelmen kívül hagy. Általában egy hozzárendelés használatával biztosíthatja, hogy az argumentumok ne null értékűek legyenek. Az alábbi kód elvetés használatával kényszeríti a hozzárendelést. A hozzárendelés jobb oldala a null szenesítési operátort használja, hogy az argumentum nullértékeként egy System.ArgumentNullException értéket dobjon. A kódnak nincs szüksége a hozzárendelés eredményére, ezért el lesz vetve. A kifejezés null értékű ellenőrzést kényszerít ki. Az elvetés egyértelművé teszi a szándékot: a hozzárendelés eredménye nem szükséges vagy nem használható.

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

    // Do work with arg.
}

Az alábbi példa önálló elvetés használatával figyelmen kívül hagyja az Task aszinkron művelet által visszaadott objektumot. A feladat hozzárendelése elnyomja azt a kivételt, amelyet a művelet a befejezéshez ér. Egyértelművé teszi a szándékot: El szeretné vetni a műveletet, és figyelmen kívül szeretné hagyni az Taskadott aszinkron műveletből eredő hibákat.

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

A feladat elvetéséhez való hozzárendelés nélkül a következő kód generál egy fordítói figyelmeztetést:

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

Feljegyzés

Ha az előző két minta valamelyikét egy hibakeresővel futtatja, a hibakereső leállítja a programot a kivétel kidobásakor. Hibakereső csatolása nélkül a kivételt mindkét esetben figyelmen kívül hagyja a rendszer.

_ is érvényes azonosító. Támogatott környezeten kívül történő használat esetén a rendszer nem elvetésként, _ hanem érvényes változóként kezeli. Ha egy elnevezett _ azonosító már hatókörben van, az önálló elvetés _ a következőt eredményezheti:

  • A hatókörön _ belüli változó értékének véletlen módosítása a tervezett elvetés értékének hozzárendelésével. Példa:
    private static void ShowValue(int _)
    {
       byte[] arr = [0, 0, 1, 2];
       _ = BitConverter.ToInt32(arr, 0);
       Console.WriteLine(_);
    }
     // The example displays the following output:
     //       33619968
    
  • Fordítóhiba a típusbiztonság megsértése miatt. Példa:
    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'
    
  • CS0136-os fordítási hiba: "Ebben a hatókörben nem deklarálható egy "_" nevű helyi vagy paraméter, mert ez a név egy helyi hatókört magában foglal egy helyi vagy paraméter definiálásához." Például:
     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
    

Lásd még