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 hetvar
trefwoord buiten de haakjes. In het volgende voorbeeld wordt typedeductie gebruikt bij het deconstrueren van de drie tuples die door deQueryCityData
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, record
en 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 Deconstruct
extensiemethoden 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 Deconstruct
out
parameter voor elke positionele parameter in de record
declaratie. Zie Positionele syntaxis voor eigenschapsdefinitie en deconstructorgedrag in afgeleide records voor meer informatie.