Koleksiyon ifadeleri - C# dil başvurusu

Ortak koleksiyon değerleri oluşturmak için koleksiyon ifadesi kullanın. Koleksiyon ifadesi, birçok farklı koleksiyon türüne atayabileceğiniz bir ters söz dizimidir. Koleksiyon ifadesi ve [ köşeli ayraçlar arasında ] bir dizi öğe içerir.

C# dili başvuru belgesi, C# dilinin en son yayımlanan sürümünü gösterir. Ayrıca, yaklaşan dil sürümü için genel önizlemelerdeki özelliklere yönelik ilk belgeleri içerir.

Belgelerde ilk olarak dilin son üç sürümünde veya geçerli genel önizlemelerde sunulan tüm özellikler tanımlanır.

Tavsiye

Bir özelliğin C# dilinde ilk tanıtıldığı zamanları bulmak için C# dil sürümü geçmişi makalesine bakın.

Aşağıdaki örnek bir System.Span<T>string öğe bildirir ve bunları haftanın günlerine başlatır:

Span<string> weekDays = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
foreach (var day in weekDays)
{
    Console.WriteLine(day);
}

Bir koleksiyon ifadesini birçok farklı koleksiyon türüne dönüştürebilirsiniz. İlk örnekte, bir koleksiyon ifadesi kullanarak bir değişkenin nasıl başlatıldığı gösterilmiştir. Aşağıdaki kod, koleksiyon ifadesi kullanabileceğiniz diğer konumların çoğunu gösterir:

// Initialize private field:
private static readonly ImmutableArray<string> _months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];

// property with expression body:
public IEnumerable<int> MaxDays =>
    [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];

public int Sum(IEnumerable<int> values) =>
    values.Sum();

public void Example()
{
    // As a parameter:
    int sum = Sum([1, 2, 3, 4, 5]);
}

Bir derleme zamanı sabitinin beklendiği bir koleksiyon ifadesi kullanamazsınız; örneğin, bir sabit başlatılırken veya bir yöntem bağımsız değişkeni için varsayılan değer olarak.

Önceki örneklerin her ikisi de koleksiyon ifadesinin öğeleri olarak sabitleri kullanıyordu. Aşağıdaki örnekte gösterildiği gibi, öğeler için değişkenleri de kullanabilirsiniz:

string hydrogen = "H";
string helium = "He";
string lithium = "Li";
string beryllium = "Be";
string boron = "B";
string carbon = "C";
string nitrogen = "N";
string oxygen = "O";
string fluorine = "F";
string neon = "Ne";
string[] elements = [hydrogen, helium, lithium, beryllium, boron, carbon, nitrogen, oxygen, fluorine, neon];
foreach (var element in elements)
{
    Console.WriteLine(element);
}

Spread öğesi

Bir koleksiyon ifadesindeki satır içi koleksiyon değerlerini almak için bir spread öğesi.. kullanın. Aşağıdaki örnek, ünlülerin bir koleksiyonunu, ünsüzlerin koleksiyonunu ve "y" harfini birleştirerek tam alfabe için bir koleksiyon oluşturur. Bu koleksiyonlardan biri şunlardan biri olabilir:

string[] vowels = ["a", "e", "i", "o", "u"];
string[] consonants = ["b", "c", "d", "f", "g", "h", "j", "k", "l", "m",
                       "n", "p", "q", "r", "s", "t", "v", "w", "x", "z"];
string[] alphabet = [.. vowels, .. consonants, "y"];

değerlendirildiğinde spread öğesi ..vowelsbeş öğe üretir: "a", "e", "i", "o"ve "u". Spread öğesi ..consonants dizideki consonants sayı olan 20 öğe üretir. Bir spread öğesindeki ifade, bir foreach deyimi kullanılarak numaralandırılabilir olmalıdır. Önceki örnekte gösterildiği gibi, yayma öğelerini bir koleksiyon ifadesindeki tek tek öğelerle birleştirebilirsiniz.

Dönüşümler

Bir koleksiyon ifadesini aşağıdakiler dahil olmak üzere farklı koleksiyon türlerine dönüştürebilirsiniz:

Uyarı

Satır içi dizileri başlatmak için koleksiyon ifadelerini kullanamazsınız. Satır içi diziler farklı başlatma söz dizimi gerektirir.

Önemli

Koleksiyon ifadesi, dönüştürmenin hedef türünden bağımsız olarak her zaman koleksiyon ifadesindeki tüm öğeleri içeren bir koleksiyon oluşturur. Örneğin, dönüştürmenin hedefi olduğunda System.Collections.Generic.IEnumerable<T>, oluşturulan kod koleksiyon ifadesini değerlendirir ve sonuçları bellek içi bir koleksiyonda depolar.

Bu davranış LINQ'tan farklıdır; burada sıra numaralandırılana kadar örneği oluşturulamayabilir. Numaralandırılamayacak sonsuz bir dizi oluşturmak için koleksiyon ifadelerini kullanamazsınız.

Derleyici, bir koleksiyon ifadesiyle bildirilen koleksiyonu oluşturmanın en yüksek performanslı yolunu belirlemek için statik çözümleme kullanır. Örneğin, boş koleksiyon ifadesi, []hedef başlatma işleminden sonra değiştirilmeyecekmiş gibi Array.Empty<T>() gerçekleştirilebilir. Hedef veya System.Span<T>System.ReadOnlySpan<T>olduğunda, depolama alanı yığın olarak ayrılmış olabilir. C# standardının §12.8.25 Koleksiyon ifadeleri yan tümcesi, derleyicinin izlemesi gereken kuralları belirtir.

Birçok API, parametre olarak birden çok koleksiyon türüyle aşırı yüklenir. Bir koleksiyon ifadesi birçok farklı ifade türüne dönüştürülebildiğinden, bu API'ler doğru dönüştürmeyi belirtmek için koleksiyon ifadesinde atamalar gerektirebilir. Aşağıdaki dönüştürme kuralları bazı belirsizlikleri giderir:

  • Daha iyi bir koleksiyon türü dönüştürme yerine daha iyi bir öğe dönüştürme tercih edilir. Başka bir deyişle, koleksiyon ifadesindeki öğelerin türü koleksiyonun türünden daha önemlidir. Bu kurallar, C# dil belirtiminin ifadeden daha iyi dönüştürme yan tümcesinde açıklanmıştır.
  • , Span<T>veya başka bir ReadOnlySpan<T> türe ref structdönüştürme, başvuru olmayan bir yapı türüne dönüştürmeden daha iyidir.
  • Bir arabirim türüne dönüştürme, arabirim türüne dönüştürmeden daha iyidir.

Bir koleksiyon ifadesini Span veya ReadOnlySpanöğesine dönüştürdüğünüzde, span nesnesinin güvenli bağlamı , yayılma alanına dahil edilen tüm öğelerin güvenli bağlamından gelir. Ayrıntılı kurallar için bkz. C# standardında §9.7.2 Başvuru güvenli bağlamları .

Koleksiyon oluşturucusu

Koleksiyon ifadeleri iyi davranan herhangi bir koleksiyon türüyle çalışır. İyi davranmış bir koleksiyon aşağıdaki özelliklere sahiptir:

  • Sayılabilir bir koleksiyondaki veya Length değeriCount, koleksiyonu numaralandırdığınızda öğe sayısıyla aynı değeri üretir.
  • Ad alanı System.Collections.Generic içindeki türler yan etkisizdir. Derleyici, bu türlerin aracı değerler olarak kullanılabildiği senaryoları iyileştirir, ancak aksi takdirde kullanıma sunmaz.
  • Bir koleksiyondaki geçerli .AddRange(x) üyeye yapılan çağrı, yineleme ve kullanarak xnumaralandırılmış değerlerinin tümünü tek tek koleksiyona ekleme ile aynı son değere .Add neden olur.

.NET çalışma zamanındaki tüm koleksiyon türleri düzgün şekilde davranır.

Uyarı

Özel bir koleksiyon türü düzgün davranmıyorsa, bu koleksiyon türünü koleksiyon ifadeleriyle kullandığınızda davranış tanımlanmamış olur.

Türleriniz, bir Create() yöntem yazarak ve oluşturucu yöntemini belirtmek için koleksiyon türüne System.Runtime.CompilerServices.CollectionBuilderAttribute özniteliğini uygulayarak koleksiyon ifadesi desteğini kabul eder. Örneğin, 80 karakterlik sabit uzunlukta arabellekler kullanan bir uygulama düşünün. Bu sınıf aşağıdaki koda benzer olabilir:

public class LineBuffer : IEnumerable<char>
{
    private readonly char[] _buffer;
    private readonly int _count;

    public LineBuffer(ReadOnlySpan<char> buffer)
    {
        _buffer = new char[buffer.Length];
        _count = buffer.Length;
        for (int i = 0; i < _count; i++)
        {
            _buffer[i] = buffer[i];
        }
    }

    public int Count => _count;
    
    public char this[int index]
    {
        get
        {
            if (index >= _count)
                throw new IndexOutOfRangeException();
            return _buffer[index];
        }
    }

    public IEnumerator<char> GetEnumerator()
    {
        for (int i = 0; i < _count; i++)
        {
            yield return _buffer[i];
        }
    }
    
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();

    // etc
}

Aşağıdaki örnekte gösterildiği gibi koleksiyon ifadeleriyle kullanmak istiyorsunuz:

LineBuffer line = ['H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '!'];

türü LineBuffer uygular IEnumerable<char>, böylece derleyici bunu bir öğe koleksiyonu char olarak tanır. Uygulanan System.Collections.Generic.IEnumerable<T> arabirimin tür parametresi, öğe türünü gösterir. Bir nesneye koleksiyon ifadeleri LineBuffer atayabilmek için uygulamanıza iki ekleme yapmanız gerekir. İlk olarak, bir yöntem içeren bir Create sınıf oluşturmanız gerekir:

internal static class LineBufferBuilder
{
    internal static LineBuffer Create(ReadOnlySpan<char> values) => new LineBuffer(values);
}

Create yöntemi bir LineBuffer nesnesi döndürmelidir ve türünün ReadOnlySpan<char>son parametresini almalıdır. öğesinin ReadOnlySpan tür parametresi, koleksiyonun öğe türüyle eşleşmelidir. Genel bir koleksiyon döndüren oluşturucu yönteminin parametresi geneldir ReadOnlySpan<T> . yöntemine ve staticerişilebilir olması gerekir.

C# 15'den başlayarak yöntemin Create parametresinden ReadOnlySpan<T> önce ek parametreleri olabilir. Koleksiyon ifadesindeki bir with(...) öğeyi kullanarak değerleri bu parametrelere geçirebilirsiniz. Ayrıntılar için bkz. Koleksiyon oluşturucu bağımsız değişkenleri .

Son olarak, sınıf bildirimine CollectionBuilderAttribute öğesini LineBuffer eklemeniz gerekir:

[CollectionBuilder(typeof(LineBufferBuilder), "Create")]

İlk parametre Builder sınıfının adını sağlar. İkinci öznitelik, oluşturucu yönteminin adını sağlar.

Koleksiyon ifadesi bağımsız değişkenleri

C# 15'den başlayarak, bir öğeyi koleksiyon ifadesindeki ilk öğe olarak kullanarak with(...) temel koleksiyonun oluşturucusunun veya fabrika yönteminin bağımsız değişkenlerini geçirebilirsiniz. Bu özellik, kapasite, karşılaştırıcılar veya diğer oluşturucu parametrelerini doğrudan koleksiyon ifadesi söz diziminde belirtmenizi sağlar. Daha fazla bilgi için bkz. koleksiyon ifadesi bağımsız değişkenleri özellik belirtimi.

with(...) öğesi koleksiyon ifadesindeki ilk öğe olmalıdır. öğesinde with(...) bildirilen bağımsız değişkenler uygun oluşturucuya gider veya hedef türü temel alan bir yöntem oluşturur. öğesindeki with bağımsız değişkenler için herhangi bir geçerli ifade kullanabilirsiniz.

Bağımsız değişken ifadeleri ve öğe ifadeleri bir kez, soldan with(...) sağa sırada değerlendirilir ve bağımsız değişkenler koleksiyon öğelerinden önce değerlendirilir. gibi bir yayılma için .. GetItems(), yayılacak koleksiyonu almak için değerlendirme çağrıları GetItems() . Numaralandırma, bu koleksiyondaki öğeleri okuyan ve sonucuna ekleyen ayrı bir adımdır. Derleyici soldan sağa konumunda değerlendirir GetItems() , ancak daha sonraki öğe ifadelerini değerlendirene kadar numaralandırmayı geciktirebilir. Daha sonraki ifadelerden önce oluşan numaralandırmanın yan etkilerine güvenmeyin.

Oluşturucu bağımsız değişkenleri

Hedef türü uygulayan System.Collections.IEnumerablebir sınıf veya yapı olduğunda içindeki bağımsız değişkenler with(...) değerlendirilir ve sonuçlar oluşturucuya gider. Derleyici, en iyi eşleşen oluşturucuyu seçmek için aşırı yükleme çözümlemesi kullanır:

public void CollectionArgumentsExamples()
{
    string[] values = ["one", "two", "three"];

    // Pass capacity argument to List<T> constructor
    List<string> names = [with(capacity: values.Length * 2), .. values];

    // Pass comparer argument to HashSet<T> constructor
    HashSet<string> set = [with(StringComparer.OrdinalIgnoreCase), "Hello", "HELLO", "hello"];
    // set contains only one element because all strings are equal with OrdinalIgnoreCase

    // Pass capacity to IList<T> (uses List<T> constructor)
    IList<int> numbers = [with(capacity: 100), 1, 2, 3];
}

Yukarıdaki örnekte:

Bu şekilde inşa edilen bir hedef türü için, with(...) türün erişilebilir parametresiz oluşturucusunun olmaması gerekir. Bağımsız değişkenleri erişilebilir bir oluşturucuya sağlamak için kullanın. Örneğin, NamedValues bir ad gerektirir ve toplama davranışını List<int>devralır:

public class NamedValues(string name) : List<int>
{
    public string Name { get; } = name;
}

Bu koleksiyon ifadesi gerekli adı sağlar ve sonra öğeleri ekler:

NamedValues readings = [with("temperature"), 18, 21, 24];
Console.WriteLine($"{readings.Name}: {string.Join(", ", readings)}");
// output: temperature: 18, 21, 24

Koleksiyon oluşturucu bağımsız değişkenleri

ile System.Runtime.CompilerServices.CollectionBuilderAttributetürler için öğesi bildirilen with(...) bağımsız değişkenleri değerlendirir ve sonuçları parametresinden önce oluşturma yöntemine ReadOnlySpan<T> gönderir. Bu özellik, oluşturma yöntemlerinin yapılandırma parametrelerini kabul etmesine olanak tanır:

internal static class MySetBuilder
{
    internal static MySet<T> Create<T>(ReadOnlySpan<T> items) => new MySet<T>(items);
    internal static MySet<T> Create<T>(IEqualityComparer<T> comparer, ReadOnlySpan<T> items) => 
        new MySet<T>(items, comparer);
}

karşılaştırıcıyı with(...) geçirmek için öğesini kullanın:

public void CollectionBuilderArgumentsExample()
{
    // Pass comparer to a type with CollectionBuilder attribute
    // The comparer argument is passed before the ReadOnlySpan<T> parameter
    MySet<string> mySet = [with(StringComparer.OrdinalIgnoreCase), "A", "a", "B"];
    // mySet contains only two elements: "A" and "B"
}

Aşırı yükleme çözümlemesi, sağladığınız bağımsız değişkenlere göre oluşturma yöntemini seçer. ReadOnlySpan<T> Koleksiyon öğelerini içeren her zaman son parametredir.

Arabirim hedef türleri

Çeşitli arabirim hedef türleri koleksiyon ifadesi bağımsız değişkenlerini destekler. Aşağıdaki tabloda desteklenen arabirimler ve bunların ilgili oluşturucu imzaları gösterilmektedir:

Arayüz Desteklenen with öğeler
IEnumerable<T>, IReadOnlyCollection<T>, IReadOnlyList<T> () (yalnızca boş)
ICollection<T>, IList<T> (), (int capacity)

ve IList<T>içinICollection<T>, derleyici belirtilen oluşturucu ile bir System.Collections.Generic.List<T> kullanır.

Restrictions

with(...) öğesi aşağıdaki kısıtlamalara sahiptir:

  • Koleksiyon ifadesindeki ilk öğe olmalıdır.
  • Bağımsız değişkenlerin türü olamaz dynamic .
  • Diziler veya span türleri (Span<T>, ReadOnlySpan<T>) için desteklenmez.
  • içindeki with(...) bağımsız değişkenler, koleksiyon ifadesinden aday hedef türüne dönüştürme olup olmadığını etkilemez. Kapsayan bir çağrı için aşırı yükleme çözümlemesi ve tür çıkarımı bu bağımsız değişkenleri yoksayar. Yalnızca bir with(...) öğenin varlığı, bağımsız değişkenleri değil, bu dönüştürmenin mevcut olup olmadığını etkiler. Hedef tür belirlendikten sonra, bağımsız değişkenler oluşturucusu veya koleksiyon oluşturucu yöntemi için aşırı yükleme çözümlemesine katılır.

Örneğin, bir karşılaştırıcı bağımsız değişkeni derleyiciye aşırı yükleme yerine List<string> aşırı yükleme seçmesini HashSet<string> söylemez:

class Examples
{
    static void Print(List<string> values) { }
    static void Print(HashSet<string> values) { }

    static void Example()
    {
        Print([with(StringComparer.OrdinalIgnoreCase), "one", "ONE"]);
    }
}

Çağrısı CS0121 üretir: "Çağrı aşağıdaki yöntemler veya özellikler arasında belirsiz." Yalnızca HashSet<string> bu karşılaştırıcıyı kabul eden bir oluşturucuya sahip olsa da, derleyici aşırı yüklemeyi seçtiğinde Print bağımsız değişkenleri yoksayarwith(...). Bu aşırı yüklemeyi seçmek için önce koleksiyon ifadesini bir HashSet<string> değişkene atayın.

Benzer şekilde, with(...) bağımsız değişkenler genel tür çıkarımı için öğe türleri sağlamaz:

class Examples
{
    static void Use<T>(List<T> values) { }

    static void Example()
    {
        Use([with(10)]);
    }
}

Bu çağrı CS0411 oluşturur: "Tür bağımsız değişkenleri kullanımdan çıkarılamaz. Tür bağımsız değişkenlerini açıkça belirtmeyi deneyin." Kapasite bağımsız değişkeni öğesini belirlemez Tve koleksiyonun çıkaracak öğesi yoktur. gibi Use<int>([with(10)])tür bağımsız değişkenini belirtin.