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. }
Dit 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. }
Vanaf C# 10 kunt u 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. Hierdoor wordt compilerfout CS8136 gegenereerd, 'Deconstruction 'var (...)' form wordt een specifiek type voor 'var' geweigerd.'
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 profiteren van C#-ondersteuning voor verwijderingen. Dit zijn alleen-schrijvenvariabelen waarvan u de waarden wilt negeren. Een verwijdering wordt gekozen door een onderstrepingsteken ("_") in een opdracht. U kunt zoveel waarden negeren als u wilt; alle worden vertegenwoordigd door de enkele verwijdering, _
.
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 geen ingebouwde ondersteuning voor het deconstrueren van andere typen dan de record
typen DictionaryEntry . 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 retourneert ongeldigheid en elke waarde die moet worden gedeconstrueerd, wordt aangegeven door een outparameter in de methodehandtekening. De volgende Deconstruct
methode van een Person
klasse retourneert bijvoorbeeld de eerste, middelste en achternaam:
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 achternaam.
- Een voornaam, middelste en achternaam.
- Een voornaam, een achternaam, 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. Elke verwijdering wordt gedefinieerd door een variabele met de naam _en één deconstructiebewerking kan meerdere verwijderingen bevatten.
In het volgende voorbeeld wordt een Person
object gedeconstrueerd in vier tekenreeksen (de voor- en achternamen, de plaats en de staat), maar wordt de achternaam 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 door de gebruiker gedefinieerde typen
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, inclusief het type, of het statisch of het exemplaar is, of deze alleen-lezen is en of deze is geïndexeerd. De tweede geeft de toegankelijkheid van de eigenschap aan. Omdat de toegankelijkheid van get- en set-accessors kan verschillen, geven Booleaanse waarden aan of de eigenschap afzonderlijke get- en set-accessors heeft en, als dat het geval is, of ze dezelfde toegankelijkheid hebben. 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 een itereert over elk element is een System.Collections.Generic.Dictionary<TKey,TValue> KeyValuePair<TKey, TValue>
en kan 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.");
}
U kunt een Deconstruct
methode toevoegen aan systeemtypen die er geen hebben. Houd rekening met de volgende extensiemethode:
public static class NullableExtensions
{
public static void Deconstruct<T>(
this T? nullable,
out bool hasValue,
out T value) where T : struct
{
hasValue = nullable.HasValue;
value = nullable.GetValueOrDefault();
}
}
Met deze extensiemethode kunnen alle Nullable<T> typen worden gedeconstrueerd in een tuple van (bool hasValue, T value)
. In het volgende voorbeeld ziet u code die gebruikmaakt van deze extensiemethode:
DateTime? questionableDateTime = default;
var (hasValue, value) = questionableDateTime;
Console.WriteLine(
$"{{ HasValue = {hasValue}, Value = {value} }}");
questionableDateTime = DateTime.Now;
(hasValue, value) = questionableDateTime;
Console.WriteLine(
$"{{ HasValue = {hasValue}, Value = {value} }}");
// Example outputs:
// { HasValue = False, Value = 1/1/0001 12:00:00 AM }
// { HasValue = True, Value = 11/10/2021 6:11:45 PM }
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.
Zie ook
Feedback
https://aka.ms/ContentUserFeedback.
Binnenkort: Gedurende 2024 worden GitHub Issues uitgefaseerd als het feedbackmechanisme voor inhoud. Dit wordt vervangen door een nieuw feedbacksysteem. Ga voor meer informatie naar:Feedback verzenden en bekijken voor