Delen via


Tuples en andere typen deconstrueren

Een tuple biedt een lichtgewicht manier om meerdere waarden op te halen uit een methode-aanroep. Maar zodra u de tuple hebt opgehaald, moet u de afzonderlijke elementen afhandelen. Werken op basis van elementen is lastig, zoals in het volgende voorbeeld wordt weergegeven. De QueryCityData methode retourneert een drie-tuple en elk van de elementen wordt toegewezen aan een variabele in een afzonderlijke bewerking.

public class Example
{
    public static void Main()
    {
        var result = QueryCityData("New York City");

        var city = result.Item1;
        var pop = result.Item2;
        var size = result.Item3;

         // Do something with the data.
    }

    private static (string, int, double) QueryCityData(string name)
    {
        if (name == "New York City")
            return (name, 8175133, 468.48);

        return ("", 0, 0);
    }
}

Het ophalen van meerdere veld- en eigenschapswaarden uit een object kan even lastig zijn: u moet een veld- of eigenschapswaarde toewijzen aan een variabele op basis van een lid per lid.

U kunt meerdere elementen ophalen uit een tuple of meerdere veld-, eigenschaps- en berekende waarden ophalen uit een object in één deconstruct-bewerking . Als u een tuple wilt deconstrueren, wijst u de elementen toe aan afzonderlijke variabelen. Wanneer u een object deconstrueert, wijst u geselecteerde waarden toe aan afzonderlijke variabelen.

Tuples

C# biedt ingebouwde ondersteuning voor het deconstrueren van tuples, waarmee u alle items in een tuple in één bewerking kunt uitpakken. De algemene syntaxis voor het deconstrueren van een tuple is vergelijkbaar met de syntaxis voor het definiëren van een tuple: u plaatst de variabelen waaraan elk element moet worden toegewezen tussen haakjes aan de linkerkant van een toewijzingsinstructie. Met de volgende instructie worden bijvoorbeeld de elementen van een vier tuple toegewezen aan vier afzonderlijke variabelen:

var (name, address, city, zip) = contact.GetAddressInfo();

Er zijn drie manieren om een tuple te deconstrueren:

  • U kunt expliciet het type van elk veld tussen haakjes declareren. In het volgende voorbeeld wordt deze methode gebruikt om de drie tuples te deconstrueren die door de QueryCityData methode worden geretourneerd.

    public static void Main()
    {
        (string city, int population, double area) = QueryCityData("New York City");
    
        // Do something with the data.
    }
    
  • U kunt het var trefwoord gebruiken zodat C# het type van elke variabele afstelt. U plaatst het var trefwoord buiten de haakjes. In het volgende voorbeeld wordt typedeductie gebruikt bij het deconstrueren van de drie tuples die door de QueryCityData methode worden geretourneerd.

    public static void Main()
    {
        var (city, population, area) = QueryCityData("New York City");
    
        // Do something with the data.
    }
    

    U kunt het var trefwoord ook afzonderlijk gebruiken met een of alle declaraties van variabelen tussen haakjes.

    public static void Main()
    {
        (string city, var population, var area) = QueryCityData("New York City");
    
        // Do something with the data.
    }
    

    Het voorgaande voorbeeld is omslachtig en wordt niet aanbevolen.

  • Ten slotte kunt u de tuple deconstrueren in variabelen die al zijn gedeclareerd.

    public static void Main()
    {
        string city = "Raleigh";
        int population = 458880;
        double area = 144.8;
    
        (city, population, area) = QueryCityData("New York City");
    
        // Do something with the data.
    }
    
  • U kunt variabeledeclaratie en toewijzing combineren in een deconstructie.

    public static void Main()
    {
        string city = "Raleigh";
        int population = 458880;
    
        (city, population, double area) = QueryCityData("New York City");
    
        // Do something with the data.
    }
    

U kunt geen specifiek type opgeven buiten de haakjes, zelfs niet als elk veld in de tuple hetzelfde type heeft. Als u dit doet, wordt compilerfout CS8136 gegenereerd: 'Deconstructie 'var (...)' vorm staat geen specifiek type toe voor 'var'.

U moet elk element van de tuple toewijzen aan een variabele. Als u elementen weglaat, genereert de compiler fout CS8132, 'Kan een tuple van 'x'-elementen niet deconstrueren in y-variabelen.'

Tuple-elementen met verwijderingen

Wanneer u een tuple deconstrueert, bent u vaak geïnteresseerd in de waarden van slechts enkele elementen. U kunt gebruikmaken van C#-ondersteuning voor wegwerpparameters, die variabelen zijn waarvan u ervoor kiest de waarden te negeren. U declareert een verwijdering met een onderstrepingsteken ("_") in een opdracht. U kunt zoveel waarden negeren als u wilt; één verwijdering, _, vertegenwoordigt alle genegeerde waarden.

In het volgende voorbeeld ziet u het gebruik van tuples met verwijderingen. De QueryCityDataForYears methode retourneert een zes tuple met de naam van een stad, het gebied, een jaar, de inwoners van de stad voor dat jaar, een tweede jaar en de bevolking van de stad voor dat tweede jaar. In het voorbeeld ziet u de verandering in de populatie tussen die twee jaar. Van de gegevens die beschikbaar zijn in de tuple, hebben we geen betrekking op het gebied van de stad, en we kennen de naam van de stad en de twee datums in ontwerptijd. Als gevolg hiervan zijn we alleen geïnteresseerd in de twee populatiewaarden die zijn opgeslagen in de tuple en kunnen we de resterende waarden verwerken als verwijderingen.

using System;

public class ExampleDiscard
{
    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

Door de gebruiker gedefinieerde typen

C# biedt ingebouwde ondersteuning voor het deconstrueren van tuple-typen, recorden DictionaryEntry typen. Als auteur van een klasse, een struct of een interface kunt u echter toestaan dat exemplaren van het type worden gedeconstrueerd door een of meer Deconstruct methoden te implementeren. De methode geeft geen waarde terug. Een uit parameter in de methodehandtekening vertegenwoordigt elke waarde die moet worden gedeconstrueerd. De volgende Deconstruct methode van een Person klasse retourneert bijvoorbeeld de eerste, middelste en familienaam:

public void Deconstruct(out string fname, out string mname, out string lname)

Vervolgens kunt u een exemplaar van de klasse met de Person naam p deconstrueren met een opdracht zoals de volgende code:

var (fName, mName, lName) = p;

In het volgende voorbeeld wordt de Deconstruct methode overbelast om verschillende combinaties van eigenschappen van een Person object te retourneren. Individuele overbelastingen retourneren:

  • Een voor- en familienaam.
  • Een eerste naam, tweede naam en familienaam.
  • Een voornaam, een familienaam, een plaatsnaam en een staatsnaam.
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 ExampleClassDeconstruction
{
    public static void Main()
    {
        var p = new Person("John", "Quincy", "Adams", "Boston", "MA");

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

Meerdere Deconstruct methoden met hetzelfde aantal parameters zijn dubbelzinnig. U moet voorzichtig zijn met het definiëren Deconstruct van methoden met verschillende aantallen parameters of 'arity'. Deconstruct methoden met hetzelfde aantal parameters kunnen niet worden onderscheiden tijdens overbelastingsresolutie.

Door de gebruiker gedefinieerd type met verwijderingen

Net als bij tuples kunt u verwijderingen gebruiken om geselecteerde items te negeren die door een Deconstruct methode worden geretourneerd. Een variabele met de naam '_' vertegenwoordigt een verwijdering. Een enkele deconstructiehandeling kan meerdere verwijderingen omvatten.

In het volgende voorbeeld wordt een Person-object gedeconstrueerd in vier tekenreeksen (de voor- en familienamen, de plaats en de staat), maar wordt de familienaam en de staat verwijderd.

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

Extensiemethoden voor deconstructie

Als u geen klasse, struct of interface hebt gemaakt, kunt u objecten van dat type nog steeds deconstrueren door een of meer Deconstructextensiemethoden te implementeren om de waarden te retourneren waarin u geïnteresseerd bent.

In het volgende voorbeeld worden twee Deconstruct extensiemethoden voor de System.Reflection.PropertyInfo klasse gedefinieerd. De eerste retourneert een set waarden die de kenmerken van de eigenschap aangeven. De tweede geeft de toegankelijkheid van de eigenschap aan. Booleaanse waarden geven aan of de eigenschap afzonderlijke get- en set-accessors heeft, of een verschillende mate van toegankelijkheid. Als er slechts één accessor is of zowel de get als de set accessor dezelfde toegankelijkheid heeft, geeft de access variabele de toegankelijkheid van de eigenschap als geheel aan. Anders worden de toegankelijkheid van de get- en set-accessors aangegeven door de getAccess en setAccess variabelen.

using System;
using System.Collections.Generic;
using System.Reflection;

public static class ReflectionExtensions
{
    public static void Deconstruct(this PropertyInfo p, out bool isStatic,
                                   out bool isReadOnly, out bool isIndexed,
                                   out Type propertyType)
    {
        var getter = p.GetMethod;

        // Is the property read-only?
        isReadOnly = ! p.CanWrite;

        // Is the property instance or static?
        isStatic = getter.IsStatic;

        // Is the property indexed?
        isIndexed = p.GetIndexParameters().Length > 0;

        // Get the property type.
        propertyType = p.PropertyType;
    }

    public static void Deconstruct(this PropertyInfo p, out bool hasGetAndSet,
                                   out bool sameAccess, out string access,
                                   out string getAccess, out string setAccess)
    {
        hasGetAndSet = sameAccess = false;
        string getAccessTemp = null;
        string setAccessTemp = null;

        MethodInfo getter = null;
        if (p.CanRead)
            getter = p.GetMethod;

        MethodInfo setter = null;
        if (p.CanWrite)
            setter = p.SetMethod;

        if (setter != null && getter != null)
            hasGetAndSet = true;

        if (getter != null)
        {
            if (getter.IsPublic)
                getAccessTemp = "public";
            else if (getter.IsPrivate)
                getAccessTemp = "private";
            else if (getter.IsAssembly)
                getAccessTemp = "internal";
            else if (getter.IsFamily)
                getAccessTemp = "protected";
            else if (getter.IsFamilyOrAssembly)
                getAccessTemp = "protected internal";
        }

        if (setter != null)
        {
            if (setter.IsPublic)
                setAccessTemp = "public";
            else if (setter.IsPrivate)
                setAccessTemp = "private";
            else if (setter.IsAssembly)
                setAccessTemp = "internal";
            else if (setter.IsFamily)
                setAccessTemp = "protected";
            else if (setter.IsFamilyOrAssembly)
                setAccessTemp = "protected internal";
        }

        // Are the accessibility of the getter and setter the same?
        if (setAccessTemp == getAccessTemp)
        {
            sameAccess = true;
            access = getAccessTemp;
            getAccess = setAccess = String.Empty;
        }
        else
        {
            access = null;
            getAccess = getAccessTemp;
            setAccess = setAccessTemp;
        }
    }
}

public class ExampleExtension
{
    public static void Main()
    {
        Type dateType = typeof(DateTime);
        PropertyInfo prop = dateType.GetProperty("Now");
        var (isStatic, isRO, isIndexed, propType) = prop;
        Console.WriteLine($"\nThe {dateType.FullName}.{prop.Name} property:");
        Console.WriteLine($"   PropertyType: {propType.Name}");
        Console.WriteLine($"   Static:       {isStatic}");
        Console.WriteLine($"   Read-only:    {isRO}");
        Console.WriteLine($"   Indexed:      {isIndexed}");

        Type listType = typeof(List<>);
        prop = listType.GetProperty("Item",
                                    BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
        var (hasGetAndSet, sameAccess, accessibility, getAccessibility, setAccessibility) = prop;
        Console.Write($"\nAccessibility of the {listType.FullName}.{prop.Name} property: ");

        if (!hasGetAndSet | sameAccess)
        {
            Console.WriteLine(accessibility);
        }
        else
        {
            Console.WriteLine($"\n   The get accessor: {getAccessibility}");
            Console.WriteLine($"   The set accessor: {setAccessibility}");
        }
    }
}
// The example displays the following output:
//       The System.DateTime.Now property:
//          PropertyType: DateTime
//          Static:       True
//          Read-only:    True
//          Indexed:      False
//
//       Accessibility of the System.Collections.Generic.List`1.Item property: public

Extensiemethode voor systeemtypen

Sommige systeemtypen bieden de Deconstruct methode als gemak. Het type biedt bijvoorbeeld System.Collections.Generic.KeyValuePair<TKey,TValue> deze functionaliteit. Wanneer u over een System.Collections.Generic.Dictionary<TKey,TValue>iterereert, is elk element een KeyValuePair<TKey, TValue> en kan het worden gedeconstrueerd. Kijk een naar het volgende voorbeeld:

Dictionary<string, int> snapshotCommitMap = new(StringComparer.OrdinalIgnoreCase)
{
    ["https://github.com/dotnet/docs"] = 16_465,
    ["https://github.com/dotnet/runtime"] = 114_223,
    ["https://github.com/dotnet/installer"] = 22_436,
    ["https://github.com/dotnet/roslyn"] = 79_484,
    ["https://github.com/dotnet/aspnetcore"] = 48_386
};

foreach (var (repo, commitCount) in snapshotCommitMap)
{
    Console.WriteLine(
        $"The {repo} repository had {commitCount:N0} commits as of November 10th, 2021.");
}

record Typen

Wanneer u een recordtype declareert met behulp van twee of meer positionele parameters, maakt de compiler een methode met een Deconstructout parameter voor elke positionele parameter in de record declaratie. Zie Positionele syntaxis voor eigenschapsdefinitie en deconstructorgedrag in afgeleide records voor meer informatie.

Zie ook