Mendekonstruksi tupel dan jenis lainnya
Tuple menyediakan cara ringan untuk mengambil beberapa nilai dari panggilan metode. Tetapi setelah Anda mengambil tuple, Anda harus menangani elemen individualnya. Bekerja berdasarkan elemen demi elemen rumit, seperti yang ditunjukkan contoh berikut. Metode QueryCityData
mengembalikan tiga tuple, dan masing-masing elemennya ditetapkan ke variabel dalam operasi terpisah.
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);
}
}
Mengambil beberapa nilai bidang dan properti dari objek bisa sama rumitnya: Anda harus menetapkan nilai bidang atau properti ke variabel berdasarkan anggota demi anggota.
Anda dapat mengambil beberapa elemen dari tuple atau mengambil beberapa bidang, properti, dan nilai komputasi dari objek dalam satu operasi dekonstruksi . Untuk mendekonstruksi tuple, Anda menetapkan elemennya ke variabel individual. Saat Anda mendekonstruksi objek, Anda menetapkan nilai yang dipilih ke variabel individual.
Tupel
C# memiliki dukungan bawaan untuk mendekonstruksi tuple, yang memungkinkan Anda membongkar semua item dalam tuple dalam satu operasi. Sintaksis umum untuk mendekonstruksi tuple mirip dengan sintaksis untuk mendefinisikan tuple tersebut: Anda menyertakan variabel tempat setiap elemen akan ditetapkan dalam tanda kurung di sisi kiri pernyataan penugasan. Misalnya, pernyataan berikut menetapkan elemen empat tuple ke empat variabel terpisah:
var (name, address, city, zip) = contact.GetAddressInfo();
Ada tiga cara untuk mendekonstruksi tuple:
Anda dapat secara eksplisit mendeklarasikan jenis setiap bidang di dalam tanda kurung. Contoh berikut menggunakan pendekatan ini untuk mendekonstruksi tiga tuple yang dikembalikan oleh metode
QueryCityData
.public static void Main() { (string city, int population, double area) = QueryCityData("New York City"); // Do something with the data. }
Anda dapat menggunakan kata kunci
var
sehingga C# menyimpulkan jenis setiap variabel. Anda menempatkan kata kuncivar
di luar tanda kurung. Contoh berikut menggunakan inferensi jenis saat mendekonstruksi tiga tuple yang dikembalikan oleh metodeQueryCityData
.public static void Main() { var (city, population, area) = QueryCityData("New York City"); // Do something with the data. }
Anda juga dapat menggunakan kata kunci
var
satu per satu dengan salah satu atau semua deklarasi variabel di dalam tanda kurung.public static void Main() { (string city, var population, var area) = QueryCityData("New York City"); // Do something with the data. }
Ini rumit dan tidak disarankan.
Terakhir, Anda dapat mendekonstruksi tuple menjadi variabel yang telah dideklarasikan.
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. }
Mulai dari C# 10, Anda dapat mencampur deklarasi variabel dan penugasan dalam dekonstruksi.
public static void Main() { string city = "Raleigh"; int population = 458880; (city, population, double area) = QueryCityData("New York City"); // Do something with the data. }
Anda tidak dapat menentukan jenis tertentu di luar tanda kurung meskipun setiap bidang dalam tuple memiliki jenis yang sama. Melakukannya akan menghasilkan kesalahan pengompilasi CS8136, formulir "Formulir 'var (...)' dekonstruksi melarang jenis tertentu untuk 'var'.".
Anda harus menetapkan setiap elemen tuple ke variabel. Jika Anda menghilangkan elemen apa pun, pengompilasi menghasilkan kesalahan CS8132, "Tidak dapat mendekonstruksi tuple elemen 'x' ke dalam variabel 'y'."
Elemen tuple dengan buang
Sering kali, ketika mendekonstruksi tuple Anda hanya tertarik pada nilai-nilai beberapa elemen. Anda dapat memanfaatkan dukungan C#untuk membuang, yang merupakan variabel hanya-tulis yang nilainya telah Anda pilih untuk diabaikan. Buang dipilih oleh karakter garis bawah ("_") dalam penugasan. Anda dapat membuang nilai sebanyak yang Anda suka; semua diwakili oleh buang tunggal, _
.
Contoh berikut mengilustrasikan penggunaan tuple dengan buang. Metode QueryCityDataForYears
ini mengembalikan enam tuple dengan nama kota, wilayahnya, setahun, populasi kota untuk tahun itu, tahun kedua, dan populasi kota untuk tahun kedua tersebut. Contoh menunjukkan perubahan populasi antara dua tahun tersebut. Dari data yang tersedia dari tuple, kami tidak peduli dengan area kota, dan kami tahu nama kota dan dua tanggal pada waktu desain. Akibatnya, kami hanya tertarik pada dua nilai populasi yang disimpan dalam tuple, dan dapat menangani nilai yang tersisa sebagai buang.
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
Jenis yang ditentukan pengguna
C# tidak menawarkan dukungan bawaan untuk mendekonstruksi jenis non-tuple selain jenis record
dan DictionaryEntry. Namun, sebagai penulis kelas, struct, atau antarmuka, Anda dapat mengizinkan instans jenis untuk didekonstruksi dengan menerapkan satu atau beberapa metode Deconstruct
. Metode mengembalikan kekosongan, dan setiap nilai yang akan didekonstruksi ditunjukkan oleh parameter keluar dalam tanda tangan metode. Misalnya, metode Deconstruct
kelas Person
berikut mengembalikan nama depan, tengah, dan belakang:
public void Deconstruct(out string fname, out string mname, out string lname)
Anda kemudian dapat mendekonstruksi instans kelas Person
bernama p
dengan penugasan seperti kode berikut:
var (fName, mName, lName) = p;
Contoh berikut membebani metode Deconstruct
untuk mengembalikan berbagai kombinasi properti objek Person
. Kelebihan beban individu mengembalikan:
- Nama depan dan belakang.
- Nama depan, tengah, dan belakang.
- Nama depan, nama belakang, nama kota, dan nama negara bagian.
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!
Beberapa metode Deconstruct
yang memiliki jumlah parameter yang sama ambigu. Anda harus berhati-hati untuk menentukan metode Deconstruct
dengan jumlah parameter yang berbeda, atau "aritas". Metode Deconstruct
dengan jumlah parameter yang sama tidak dapat dibedakan selama resolusi kelebihan beban.
Jenis yang ditentukan pengguna dengan buang
Sama seperti yang Anda lakukan dengan tuple, Anda dapat menggunakan buang untuk mengabaikan item yang dipilih yang dikembalikan oleh metode Deconstruct
. Setiap pembuangan didefinisikan oleh variabel bernama "_", dan satu operasi dekonstruksi dapat mencakup beberapa pembuangan.
Contoh berikut mendekonstruksi objek Person
menjadi empat string (nama depan dan belakang, kota, dan status) tetapi membuang nama belakang dan status.
// Deconstruct the person object.
var (fName, _, city, _) = p;
Console.WriteLine($"Hello {fName} of {city}!");
// The example displays the following output:
// Hello John of Boston!
Metode ekstensi untuk jenis yang ditentukan pengguna
Jika Anda tidak menulis kelas, struktur, atau antarmuka, Anda masih dapat mendekonstruksi objek jenis tersebut dengan menerapkan satu atau beberapa Deconstruct
metode ekstensi untuk mengembalikan nilai yang Anda minati.
Contoh berikut mendefinisikan dua metode ekstensi Deconstruct
untuk kelas System.Reflection.PropertyInfo. Yang pertama mengembalikan sekumpulan nilai yang menunjukkan karakteristik properti, termasuk jenisnya, baik statis atau instans, baik itu baca-saja, dan apakah itu diindeks. Yang kedua menunjukkan aksesibilitas properti. Karena aksesibilitas pengakses get dan set dapat berbeda, nilai Boolean menunjukkan apakah properti memiliki pengakses get dan set terpisah dan, jika ya, apakah mereka memiliki aksesibilitas yang sama. Jika hanya ada satu pengakses atau pengakses get dan set memiliki aksesibilitas yang sama, variabel access
menunjukkan aksesibilitas properti secara keseluruhan. Jika tidak, aksesibilitas pengakses get dan set ditunjukkan oleh variabel getAccess
dan setAccess
.
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
Metode ekstensi untuk jenis sistem
Beberapa jenis sistem menyediakan metode Deconstruct
sebagai kenyamanan. Misalnya, jenis System.Collections.Generic.KeyValuePair<TKey,TValue> menyediakan fungsionalitas ini. Saat Anda mengulangi System.Collections.Generic.Dictionary<TKey,TValue> setiap elemen adalah KeyValuePair<TKey, TValue>
dan dapat didekonstruksi. Pertimbangkan contoh berikut:
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.");
}
Anda dapat menambahkan metode Deconstruct
ke jenis sistem yang tidak memilikinya. Pertimbangkan metode ekstensi berikut:
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();
}
}
Metode ekstensi ini memungkinkan semua jenis Nullable<T> untuk didekonstruksi menjadi tuple (bool hasValue, T value)
. Contoh berikut menunjukkan kode yang menggunakan metode ekstensi ini:
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 }
Jenis record
Saat Anda mendeklarasikan jenis rekaman dengan menggunakan dua parameter posisi atau lebih, pengompilasi membuat metode Deconstruct
dengan parameter out
untuk setiap parameter posisi dalam deklarasi record
. Untuk informasi selengkapnya, lihat Sintaksis posisi untuk definisi properti dan Perilaku dekonstruktor dalam rekaman turunan.
Lihat juga
Saran dan Komentar
https://aka.ms/ContentUserFeedback.
Segera hadir: Sepanjang tahun 2024 kami akan menghentikan penggunaan GitHub Issues sebagai mekanisme umpan balik untuk konten dan menggantinya dengan sistem umpan balik baru. Untuk mengetahui informasi selengkapnya, lihat:Kirim dan lihat umpan balik untuk