Demetleri ve diğer türleri ayrıştırma
Tanımlama grubu, bir yöntem çağrısından birden çok değer almak için basit bir yol sağlar. Ancak tanımlama grubu alındıktan sonra kendi öğelerini işlemeniz gerekir. Aşağıdaki örnekte gösterildiği gibi, öğeye göre bir temel üzerinde çalışmak hantaldır. QueryCityData
yöntemi üç tanımlama grubu döndürür ve öğelerinin her biri ayrı bir işlemde bir değişkene atanır.
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);
}
}
Bir nesneden birden çok alan ve özellik değeri almak da aynı derecede hantal olabilir: Bir değişkene üye bazında alan veya özellik değeri atamanız gerekir.
Bir tanımlama grubundan birden çok öğe alabilir veya tek bir yapı kaldırma işlemindeki bir nesneden birden çok alan, özellik ve hesaplanan değer alabilirsiniz. Bir tanımlama kümesinin yapısını çözmek için öğelerini bağımsız değişkenlere atarsınız. Bir nesnenin yapısını kaldırdığınızda, seçilen değerleri tek tek değişkenlere atarsınız.
Tanımlama grupları
C#, bir tanımlama grubundaki tüm öğeleri tek bir işlemle paketten çıkarmanıza olanak tanıyan yerleşik tanımlama grubu oluşturma desteğine sahiptir. Tanımlama grubu oluşturmanın genel söz dizimi, tanımlama söz dizimine benzer: her öğenin atanacağı değişkenleri atama deyiminin sol tarafındaki parantez içine alırsınız. Örneğin, aşağıdaki deyim dörtli bir tanımlama grubunun öğelerini dört ayrı değişkene atar:
var (name, address, city, zip) = contact.GetAddressInfo();
Bir tanımlama kümesinin yapısını kaldırmanın üç yolu vardır:
Her alanın türünü parantez içinde açıkça bildirebilirsiniz. Aşağıdaki örnek, yöntemi tarafından
QueryCityData
döndürülen üç tanımlama grubu yapısını çözmek için bu yaklaşımı kullanır.public static void Main() { (string city, int population, double area) = QueryCityData("New York City"); // Do something with the data. }
C# öğesinin her değişkenin
var
türünü çıkarması için anahtar sözcüğünü kullanabilirsiniz. Anahtar sözcüğü parantezlerin dışına yerleştirirsinizvar
. Aşağıdaki örnek, yöntemi tarafındanQueryCityData
döndürülen üç tanımlama grubu kaldırıldığında tür çıkarımı kullanır.public static void Main() { var (city, population, area) = QueryCityData("New York City"); // Do something with the data. }
Anahtar sözcüğünü
var
parantez içindeki değişken bildirimlerinden herhangi biriyle veya tümüyle tek tek de kullanabilirsiniz.public static void Main() { (string city, var population, var area) = QueryCityData("New York City"); // Do something with the data. }
Bu hantaldır ve önerilmez.
Son olarak, tanımlama demetini önceden bildirilmiş olan değişkenlere dönüştürebilirsiniz.
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. }
C# 10'da başlayarak, değişken bildirimini ve atamayı bir yapısızlaştırmada karıştırabilirsiniz.
public static void Main() { string city = "Raleigh"; int population = 458880; (city, population, double area) = QueryCityData("New York City"); // Do something with the data. }
Tanımlama grubundaki her alan aynı türe sahip olsa bile parantez dışında belirli bir tür belirtemezsiniz. Bunu yaptığınızda CS8136, "'var (...)' yapısızlaştırma formu 'var' için belirli bir türe izin verilmiyorsa" derleyici hatası oluşturur.
Tanımlama grubunun her öğesini bir değişkene atamanız gerekir. Herhangi bir öğeyi atlarsanız, derleyici CS8132 "'x' öğelerinin bir demetini 'y' değişkenlerine dönüştüremez" hatasını oluşturur.
Atılan öğelerle tanımlama grubu
Genellikle bir tanımlama grubu oluştururken yalnızca bazı öğelerin değerleriyle ilgilenirsiniz. Değerlerini yoksaymayı seçtiğiniz yalnızca yazma değişkenleri olan atmalar için C# desteğinden yararlanabilirsiniz. Atamadaki bir alt çizgi karakteri ("_") tarafından atma seçilir. İstediğiniz kadar değer atabilirsiniz; tümü tek atma _
ile temsil edilir.
Aşağıdaki örnekte, atmalarla tanımlama demetlerinin kullanımı gösterilmektedir. QueryCityDataForYears
yöntemi, bir şehir, onun alanı, bir yıl, o yıl için şehir nüfusu, ikinci yıl ve o ikinci yıl için şehrin nüfusu ile altılı bir tanımlama grubu döndürür. Örnekte, bu iki yıl arasındaki nüfus değişikliği gösterilmektedir. Tanımlama grubundaki verilerden şehir alanıyla ilgili bilgimiz yok ve tasarım zamanında şehir adını ve iki tarihi biliyoruz. Sonuç olarak, yalnızca tanımlama grubunda depolanan iki popülasyon değeriyle ilgileniyoruz ve kalan değerlerini atılmış olarak işleyebiliriz.
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
Kullanıcı tanımlı türler
C#, ve DictionaryEntry türleri dışındaki record
tanımlama grubu olmayan türlerin yapısını kaldırmaya yönelik yerleşik destek sunmaz. Ancak, bir sınıfın, yapının veya arabirimin yazarı olarak, bir veya daha fazla Deconstruct
yöntem uygulayarak tür örneklerinin yapılandırılmasına izin vekleyebilirsiniz. yöntemi void döndürür ve yapısı kaldırılacak her değer, yöntem imzasında bir out parametresiyle gösterilir. Örneğin, bir Person
sınıfın aşağıdaki Deconstruct
yöntemi ad, ikinci ve soyadını döndürür:
public void Deconstruct(out string fname, out string mname, out string lname)
Daha sonra aşağıdaki kod gibi bir atamayla adlı p
sınıfın örneğini Person
dekonstrüde edebilirsiniz:
var (fName, mName, lName) = p;
Aşağıdaki örnek, bir Person
nesnenin Deconstruct
çeşitli özellik bileşimlerini döndürmek için yöntemini aşırı yükler. Tek tek aşırı yüklemeler döndürür:
- Bir ad ve soyadı.
- Ad, ikinci ve soyadı.
- Ad, soyadı, şehir adı ve eyalet adı.
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!
Aynı sayıda parametreye sahip birden çok Deconstruct
yöntem belirsizdir. Farklı sayıda parametre veya "arity" içeren yöntemleri tanımlamaya Deconstruct
dikkat etmeniz gerekir. Deconstruct
aynı sayıda parametreye sahip yöntemler aşırı yükleme çözümlemesi sırasında ayırt edilemez.
Atmalarla kullanıcı tanımlı tür
Tanımlama listelerinde olduğu gibi, bir Deconstruct
yöntem tarafından döndürülen seçili öğeleri yoksaymak için atmaları kullanabilirsiniz. Her atma işlemi "_" adlı bir değişken tarafından tanımlanır ve tek bir yapılandırma işlemi birden çok atma içerebilir.
Aşağıdaki örnek, bir Person
nesneyi dört dizeye (ad ve soyadı, şehir ve eyalet) ayırır, ancak soyadını ve eyaleti atar.
// Deconstruct the person object.
var (fName, _, city, _) = p;
Console.WriteLine($"Hello {fName} of {city}!");
// The example displays the following output:
// Hello John of Boston!
Kullanıcı tanımlı türler için uzantı yöntemleri
Bir sınıf, yapı veya arabirim yazmadıysanız, ilgilendiğiniz değerleri döndürmek için bir veya daha fazla Deconstruct
uzantı yöntemi uygulayarak bu türdeki nesnelerin yapısını yine de kaldırabilirsiniz.
Aşağıdaki örnek, sınıfı için System.Reflection.PropertyInfo iki Deconstruct
uzantı yöntemini tanımlar. birincisi, türü, statik veya örnek, salt okunur olup olmadığı ve dizine eklenip dizinlenmediği dahil olmak üzere özelliğin özelliklerini gösteren bir değer kümesi döndürür. İkincisi özelliğin erişilebilirliğini gösterir. Alma ve ayarlama erişimcilerinin erişilebilirliği farklı olabileceğinden, Boole değerleri özelliğin ayrı alma ve ayarlama erişimcileri olup olmadığını ve varsa aynı erişilebilirliği olup olmadığını gösterir. Yalnızca bir erişimci varsa veya hem get hem de küme erişimcisi aynı erişilebilirliği varsa, access
değişken özelliğin erişilebilirliğini bir bütün olarak gösterir. Aksi takdirde, get ve set erişimcilerinin erişilebilirliği ve setAccess
değişkenleri tarafından getAccess
belirtilir.
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
Sistem türleri için uzantı yöntemi
Bazı sistem türleri kolaylık sağlamak için Deconstruct
yöntemini sağlar. Örneğin, System.Collections.Generic.KeyValuePair<TKey,TValue> türü bu işlevi sağlar. Bir öğe üzerinde System.Collections.Generic.Dictionary<TKey,TValue> yineleme yaptığınızda bir öğesidir KeyValuePair<TKey, TValue>
ve yapısızlaştırılabilir. Aşağıdaki örneği inceleyin:
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.");
}
Olmayan sistem türlerine bir Deconstruct
yöntem ekleyebilirsiniz. Aşağıdaki uzantı yöntemini göz önünde bulundurun:
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();
}
}
Bu uzantı yöntemi, tüm Nullable<T> türlerin bir tanımlama grubuna (bool hasValue, T value)
dönüştürülmesini sağlar. Aşağıdaki örnekte bu uzantı yöntemini kullanan kod gösterilmektedir:
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
Tür
İki veya daha fazla konumsal parametre kullanarak bir kayıt türü bildirdiğinizde, derleyici bildirimdeki record
her konum parametresi için bir parametre içeren bir Deconstruct
out
yöntem oluşturur. Daha fazla bilgi için bkz. Türetilmiş kayıtlarda özellik tanımı ve Yıkıcı davranışı için konumsal söz dizimi.