A csonkok és más típusok lebontása
A tuple egyszerű módot kínál több érték lekérésére egy metódushívásból. De miután lekérte a rekordot, kezelnie kell az egyes elemeket. Az elemenkénti munka nehézkes, ahogy az alábbi példa is mutatja. A QueryCityData
metódus három rekordot ad vissza, és minden eleme egy változóhoz van rendelve egy külön műveletben.
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);
}
}
Egy objektumból egyszerre több mező- és tulajdonságérték beolvasása is nehézkes lehet: tagonként kell hozzárendelni egy mezőt vagy tulajdonságértéket egy változóhoz.
Több elemet is lekérhet egy rekordból, vagy több mezőt, tulajdonságot és számított értéket is lekérhet egy objektumból egyetlen dekonstruálási műveletben. Egy rekord dekonstruálásához az elemeket az egyes változókhoz rendelheti. Objektum dekonstruálásakor a kijelölt értékeket az egyes változókhoz rendeli.
Rekordok
A C# beépített támogatással rendelkezik a rekordok dekonstruálásához, amely lehetővé teszi, hogy egyetlen műveletben csomagolja ki az összes elemet. A tuple dekonstruálásának általános szintaxisa hasonló a definíció szintaxisához: a hozzárendelési utasítás bal oldalán zárójelbe kell tenni azokat a változókat, amelyekhez az egyes elemeket hozzá kell rendelni. A következő utasítás például négy különböző változóhoz rendeli a négy rekord elemeit:
var (name, address, city, zip) = contact.GetAddressInfo();
A tuple-ok bontásának három módja van:
Az egyes mezők típusát zárójelek között explicit módon deklarálhatja. Az alábbi példa ezt a megközelítést használja a metódus által visszaadott három rekord dekonstruálásához
QueryCityData
.public static void Main() { (string city, int population, double area) = QueryCityData("New York City"); // Do something with the data. }
Használhatja a kulcsszót, hogy a
var
C# az egyes változók típusát következtethesse. A kulcsszót avar
zárójeleken kívülre kell helyeznie. Az alábbi példa típuskövetkezést használ a metódus általQueryCityData
visszaadott három rekord dekonstruálásakor.public static void Main() { var (city, population, area) = QueryCityData("New York City"); // Do something with the data. }
A kulcsszót
var
külön-külön is használhatja a zárójelekben található változódeklarációk bármelyikével vagy mindegyikével.public static void Main() { (string city, var population, var area) = QueryCityData("New York City"); // Do something with the data. }
Az előző példa nehézkes, és nem ajánlott.
Végül dekonstruálhatja a tömböt a már deklarált változókra.
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. }
A dekonstruálásban keverheti a változó deklarációt és hozzárendelést.
public static void Main() { string city = "Raleigh"; int population = 458880; (city, population, double area) = QueryCityData("New York City"); // Do something with the data. }
A zárójeleken kívül nem adhat meg egy adott típust, még akkor sem, ha a rekord minden mezőjének ugyanaz a típusa. Ezzel létrehoz egy CS8136-os fordítói hibát, a "Deconstruction 'var (...)' űrlap letilt egy adott típust a "var" számára."
A rekord minden elemét hozzá kell rendelnie egy változóhoz. Ha kihagy bármilyen elemet, a fordító a CS8132 hibát generálja: "Nem lehet "x" elemeket "y" változókká bontani."
Elvetéssel rendelkező elemek lefedése
A tuple-ok lebontásakor gyakran csak bizonyos elemek értékei érdeklik. Kihasználhatja a C# támogatását a elvetéseknél, amelyek csak írásra szolgáló változók, amelyek értékeit figyelmen kívül hagy. Deklarálhat egy elvetést aláhúzásjellel ("_") egy hozzárendelésben. Annyi értéket elvethet, amennyit csak szeretne; egyetlen elvetés( _
) az összes elvetett értéket jelöli.
Az alábbi példa a kukák elvetéssel való használatát mutatja be. A QueryCityDataForYears
módszer egy hat-rekordot ad vissza egy város, annak területe, egy év, az adott év lakossága, egy második év és a város népessége a második évben. 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.
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
Felhasználó által definiált típusok
A C# beépített támogatást nyújt a tuple típusok, record
és DictionaryEntry típusok dekonstruálásához. Az osztály, a szerkezet vagy a felület szerzőjeként azonban egy vagy több Deconstruct
metódus implementálásával engedélyezheti az ilyen típusú példányok dekonstruálását. A metódus ürességet ad vissza. A és paraméterek a metódusaláírásban az egyes lebontandó értékeket jelölik. Egy Deconstruct
osztály következő Person
metódusa például az első, a középső és a családnevet adja vissza:
public void Deconstruct(out string fname, out string mname, out string lname)
Ezután a következő kódhoz hasonló hozzárendeléssel elnevezett Person
osztálypéldányt p
dekonstruálhat:
var (fName, mName, lName) = p;
Az alábbi példa túlterheli a metódust egy Deconstruct
objektum különböző tulajdonságainak visszaadásához Person
. Az egyéni túlterhelések visszatérnek:
- Utónév és családnév.
- Első, középső és családi név.
- Utónév, családnév, városnév és államnév.
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!
Több Deconstruct
azonos számú paraméterrel rendelkező metódus nem egyértelmű. Ügyeljen arra, hogy különböző számú paramétert vagy "aritást" tartalmazó metódusokat definiáljon Deconstruct
.
Deconstruct
azonos számú paraméterrel rendelkező metódusok nem különböztethetők meg a túlterhelés feloldása során.
Felhasználó által definiált típus elvetéssel
Ugyanúgy, mint a csuplok esetében, elvetéssel figyelmen kívül hagyhatja a Deconstruct
metódus által visszaadott kijelölt elemeket. A "_" nevű változó elvetésnek felel meg. Egyetlen bontási művelet több elvetést is tartalmazhat.
Az alábbi példa egy Person
objektumot négy sztringre (az utó- és családnevekre, a városra és az államra) bont, de elveti a családnevet és az államot.
// 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 dekonstruálás kiterjesztési módszerei
Ha nem készített osztályt, szerkezetet vagy felületet, akkor is dekonstruálhatja az ilyen típusú objektumokat egy vagy több Deconstruct
bővítménymetódus implementálásával, hogy visszaadja azokat az értékeket, amelyekben érdekli.
Az alábbi példa két Deconstruct
bővítménymetelyt határoz meg az System.Reflection.PropertyInfo osztályhoz. Az első olyan értékkészletet ad vissza, amely a tulajdonság jellemzőit jelzi. A második a tulajdonság akadálymentességét jelzi. A logikai értékek azt jelzik, hogy a tulajdonság külön beolvasási és beállítási tartozékokkal vagy különböző kisegítő lehetőségekkel rendelkezik-e. Ha csak egy tartozék van, vagy a get és a készlet tartozéka is ugyanazzal az akadálymentességgel rendelkezik, a access
változó a tulajdonság egészének akadálymentességét jelzi. Ellenkező esetben a get és a set tartozékok akadálymentességét a változók és getAccess
a setAccess
változók jelzik.
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
Rendszertípusok bővítménymetódusa
Egyes rendszertípusok kényelmesen biztosítják a Deconstruct
metódust. A típus például ezt a System.Collections.Generic.KeyValuePair<TKey,TValue> funkciót biztosítja. Amikor egy System.Collections.Generic.Dictionary<TKey,TValue>-t iterálsz, minden elem egy KeyValuePair<TKey, TValue>
, és dekonstruálható. Vegyük a következő példát:
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
Típusok
Amikor két vagy több pozícióparaméter használatával deklarál egy rekordtípust További információ: A tulajdonságdefiníció és a Deconstructor viselkedésének pozíciószintaxisa származtatott rekordokban.