Aracılığıyla paylaş


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ştirirsiniz var . Aşağıdaki örnek, yöntemi tarafından QueryCityData 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.
    }
    

    Yukarıdaki örnek hantaldır ve önerilmez.

  • Son olarak, tanımlama demetini önceden bildirilen 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.
    }
    
  • Değişken bildirimi ve atamasını bir yapısızlaştırmada birlikte kullanabilirsiniz.

    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 vermemektedir" 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. C#'nin atmadesteğinden yararlanabilirsiniz. Bunlar, değerlerini yoksaymayı seçtiğiniz salt yazma değişkenleridir. Bir atamada alt çizgi karakteri ("_") ile bir atılma bildirirsiniz. İstediğiniz kadar değer iptal edebilirsiniz; yalnızca _, iptal edilen tüm değerleri temsil eder.

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# tanımlama grubu türlerini decompose etmek, recordve DictionaryEntry türleri için yerleşik destek sunar. 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öntem void döndürür. Yöntem imzasında parametresi, yapısı kaldırılacak her değeri temsil eder. Örneğin, bir Deconstruct sınıfının aşağıdaki Person yöntemi ilk, orta ve aile adı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ı Person sınıfın örneğini p dekonstrüde edebilirsiniz:

var (fName, mName, lName) = p;

Aşağıdaki örnek, bir Deconstruct nesnenin Person ç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ı.
  • İlk isim, orta isim ve soyadı.
  • Ad, aile adı, ş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. Aynı sayıda parametreye sahip Deconstruct yöntemleri 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. "_" adlı değişken bir atma temsil eder. Tek bir yapısızlaştırma işlemi birden çok ihmal veya iptal içerebilir.

Aşağıdaki örnek, bir Person nesnesini dört dizeye (ad ve aile adları, şehir ve eyalet) ayırır, ancak aile adını ve durumu 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!

Yapısızlaştırma uzantısı 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 Deconstructuzantı yöntemi uygulayarak bu türdeki nesnelerin yapısını yine de kaldırabilirsiniz.

Aşağıdaki örnek, sınıfı için Deconstruct iki System.Reflection.PropertyInfo uzantı yöntemini tanımlar. birincisi, özelliğin özelliklerini gösteren bir değer kümesi döndürür. İkincisi özelliğin erişilebilirliğini gösterir. Boole değerleri özelliğin ayrı alma ve ayarlama erişimcilerine veya farklı erişilebilirlik özelliklerine sahip 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 getAccess değişkenleri tarafından setAccess 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 System.Collections.Generic.Dictionary<TKey,TValue>üzerinde yineleme yaparken, her öğe bir KeyValuePair<TKey, TValue> ve yapısı kaldı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.");
}

record Tür

İki veya daha fazla konumsal parametre kullanarak bir kayıt türü bildirdiğinizde, derleyici bildirimdeki Deconstruct her konum parametresi için bir parametre içeren bir outrecord 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.

Ayrıca bkz.