Bagikan melalui


Ekspresi koleksi - Referensi bahasa C#

Gunakan ekspresi koleksi untuk membuat nilai koleksi umum. Ekspresi koleksi adalah sintaks terse yang dapat Anda tetapkan ke berbagai jenis koleksi. Ekspresi koleksi berisi urutan elemen antara [ dan ] tanda kurung siku.

Referensi bahasa C# mendokumentasikan versi bahasa C# yang paling baru dirilis. Ini juga berisi dokumentasi awal untuk fitur dalam pratinjau publik untuk rilis bahasa yang akan datang.

Dokumentasi mengidentifikasi fitur apa pun yang pertama kali diperkenalkan dalam tiga versi terakhir bahasa atau dalam pratinjau publik saat ini.

Petunjuk / Saran

Untuk menemukan kapan fitur pertama kali diperkenalkan di C#, lihat artikel tentang riwayat versi bahasa C#.

Contoh berikut mendeklarasikan System.Span<T> elemen string dan menginisialisasinya ke hari dalam seminggu:

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

Anda dapat mengonversi ekspresi koleksi ke berbagai jenis koleksi. Contoh pertama menunjukkan cara menginisialisasi variabel dengan menggunakan ekspresi koleksi. Kode berikut menunjukkan banyak lokasi lain di mana Anda bisa menggunakan ekspresi koleksi:

// 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]);
}

Anda tidak dapat menggunakan ekspresi koleksi di mana konstanta waktu kompilasi diharapkan, seperti saat menginisialisasi konstanta, atau sebagai nilai default untuk argumen metode.

Kedua contoh sebelumnya menggunakan konstanta sebagai elemen ekspresi koleksi. Anda juga dapat menggunakan variabel untuk elemen, seperti yang ditunjukkan dalam contoh berikut:

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);
}

Menyebarkan elemen

Gunakan elemen.. spread untuk nilai koleksi sebaris dalam ekspresi koleksi. Contoh berikut membuat koleksi untuk alfabet lengkap dengan menggabungkan koleksi vokal, koleksi konsonan, dan huruf "y", yang dapat berupa:

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"];

Elemen ..vowelsspread , ketika dievaluasi, menghasilkan lima elemen: "a", , "e", "i""o", dan "u". Elemen ..consonants spread menghasilkan 20 elemen, angka dalam consonants array. Ekspresi dalam elemen spread harus dapat dijumlahkan dengan menggunakan foreach pernyataan. Seperti yang ditunjukkan pada contoh sebelumnya, Anda dapat menggabungkan elemen spread dengan elemen individual dalam ekspresi koleksi.

Konversi

Anda dapat mengonversi ekspresi koleksi ke jenis koleksi yang berbeda, termasuk:

Nota

Anda tidak dapat menggunakan ekspresi koleksi untuk menginisialisasi array sebaris. Array sebaris memerlukan sintaks inisialisasi yang berbeda.

Penting

Ekspresi koleksi selalu membuat koleksi yang menyertakan semua elemen dalam ekspresi koleksi, terlepas dari jenis target konversi. Misalnya, ketika target konversi adalah System.Collections.Generic.IEnumerable<T>, kode yang dihasilkan mengevaluasi ekspresi koleksi dan menyimpan hasilnya dalam koleksi dalam memori.

Perilaku ini berbeda dari LINQ, di mana urutan mungkin tidak dibuat sampai dijumlahkan. Anda tidak dapat menggunakan ekspresi koleksi untuk menghasilkan urutan tak terbatas yang tidak akan dijumlahkan.

Pengkompilasi menggunakan analisis statis untuk menentukan cara paling berkinerja untuk membuat koleksi yang dideklarasikan dengan ekspresi koleksi. Misalnya, ekspresi koleksi kosong, [], dapat direalisasikan seolah-olah Array.Empty<T>() target tidak akan dimodifikasi setelah inisialisasi. Ketika target adalah System.Span<T> atau System.ReadOnlySpan<T>, penyimpanan mungkin dialokasikan tumpukan. Spesifikasi fitur ekspresi koleksi menentukan aturan yang harus diikuti pengkompilasi.

Banyak API kelebihan beban dengan beberapa jenis koleksi sebagai parameter. Karena ekspresi koleksi dapat dikonversi ke berbagai jenis ekspresi, API ini mungkin memerlukan transmisi pada ekspresi koleksi untuk menentukan konversi yang benar. Aturan konversi berikut menyelesaikan beberapa ambiguitas:

  • Konversi elemen yang lebih baik lebih disukai daripada konversi jenis koleksi yang lebih baik. Dengan kata lain, jenis elemen dalam ekspresi koleksi memiliki kepentingan lebih dari jenis koleksi. Aturan ini dijelaskan dalam spesifikasi fitur untuk konversi yang lebih baik dari ekspresi koleksi.
  • Konversi ke Span<T>, ReadOnlySpan<T>, atau jenis lain ref struct lebih baik daripada konversi ke jenis struct non-ref.
  • Konversi ke jenis noninterface lebih baik daripada konversi ke jenis antarmuka.

Saat Anda mengonversi ekspresi koleksi menjadi Span atau ReadOnlySpan, konteks aman objek rentang berasal dari konteks aman semua elemen yang disertakan dalam rentang. Untuk aturan terperinci, lihat Spesifikasi ekspresi koleksi.

Pembuat koleksi

Ekspresi koleksi berfungsi dengan jenis koleksi apa pun yang bersifat baik. Koleksi yang ber perilaku baik memiliki karakteristik berikut:

  • Nilai Count atau Length pada koleksi yang dapat dihitung menghasilkan nilai yang sama dengan jumlah elemen saat dijumlahkan.
  • Jenis di System.Collections.Generic namespace layanan bebas efek samping. Pengkompilasi dapat mengoptimalkan skenario di mana jenis ini dapat digunakan sebagai nilai perantara, tetapi tidak mengeksposnya sebaliknya.
  • Panggilan ke anggota yang berlaku .AddRange(x) pada koleksi menghasilkan nilai akhir yang sama seperti iterasi dan x menambahkan semua nilai enumerasinya satu per satu ke koleksi dengan menggunakan .Add.

Semua jenis koleksi dalam runtime .NET berperilaku baik.

Peringatan

Jika jenis koleksi kustom tidak bersifat baik, perilaku tidak terdefinisi saat Anda menggunakan jenis koleksi tersebut dengan ekspresi koleksi.

Jenis Anda memilih untuk mengumpulkan dukungan ekspresi dengan menulis Create() metode dan menerapkan System.Runtime.CompilerServices.CollectionBuilderAttribute atribut pada jenis koleksi untuk menunjukkan metode penyusun. Misalnya, pertimbangkan aplikasi yang menggunakan buffer panjang tetap 80 karakter. Kelas tersebut mungkin terlihat seperti kode berikut:

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
}

Anda ingin menggunakannya dengan ekspresi koleksi seperti yang diperlihatkan dalam sampel berikut:

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

Jenis mengimplementasikan LineBufferIEnumerable<char>, sehingga pengkompilasi mengenalinya sebagai kumpulan char item. Parameter jenis antarmuka yang diimplementasikan System.Collections.Generic.IEnumerable<T> menunjukkan jenis elemen. Anda perlu membuat dua tambahan ke aplikasi Anda untuk dapat menetapkan ekspresi koleksi ke LineBuffer objek. Pertama, Anda perlu membuat kelas yang berisi Create metode:

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

Metode Create harus mengembalikan LineBuffer objek, dan harus mengambil parameter akhir dari jenis ReadOnlySpan<char>. Parameter ReadOnlySpan jenis harus cocok dengan jenis elemen koleksi. Metode penyusun yang mengembalikan koleksi generik memiliki generik ReadOnlySpan<T> sebagai parameternya. Metode harus dapat diakses dan static.

Mulai dari C# 15, Create metode dapat memiliki parameter tambahan sebelum ReadOnlySpan<T> parameter . Anda dapat meneruskan nilai ke parameter ini dengan menggunakan with(...) elemen dalam ekspresi koleksi. Lihat Argumen penyusun koleksi untuk detailnya.

Terakhir, Anda harus menambahkan CollectionBuilderAttribute ke LineBuffer deklarasi kelas:

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

Parameter pertama menyediakan nama kelas Builder . Atribut kedua menyediakan nama metode penyusun.

Argumen ekspresi koleksi

Mulai dari C# 15, Anda dapat meneruskan argumen ke konstruktor atau metode pabrik koleksi yang mendasar dengan menggunakan with(...) elemen sebagai elemen pertama dalam ekspresi koleksi. Fitur ini memungkinkan Anda menentukan kapasitas, pembanding, atau parameter konstruktor lainnya langsung dalam sintaks ekspresi koleksi. Untuk informasi selengkapnya, lihat spesifikasi fitur argumen ekspresi koleksi.

Elemen with(...) harus menjadi elemen pertama dalam ekspresi koleksi. Argumen yang dideklarasikan dalam elemen diteruskan with(...) ke konstruktor yang sesuai atau membuat metode berdasarkan jenis target. Anda dapat menggunakan ekspresi valid apa pun untuk argumen dalam with elemen .

Argumen konstruktor

Ketika jenis target adalah kelas atau struktur yang mengimplementasikan System.Collections.IEnumerable, argumen di dievaluasi dan hasilnya diteruskan with(...) ke konstruktor. Pengkompilasi menggunakan resolusi kelebihan beban untuk memilih konstruktor yang paling cocok:

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];
}

Dalam contoh sebelumnya:

Argumen penyusun koleksi

Untuk jenis dengan System.Runtime.CompilerServices.CollectionBuilderAttribute, argumen yang dideklarasikan dalam with(...) elemen dievaluasi dan hasilnya diteruskan ke metode buat sebelumReadOnlySpan<T> parameter . Fitur ini memungkinkan membuat metode untuk menerima parameter konfigurasi:

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);
}

Anda kemudian dapat menggunakan with(...) elemen untuk meneruskan pembanding:

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"
}

Metode buat dipilih menggunakan resolusi kelebihan beban berdasarkan argumen yang disediakan. yang ReadOnlySpan<T> berisi elemen koleksi selalu merupakan parameter terakhir.

Jenis target antarmuka

Beberapa jenis target antarmuka mendukung argumen ekspresi koleksi. Tabel berikut ini memperlihatkan antarmuka yang didukung dan tanda tangan konstruktor yang berlaku:

Antarmuka Elemen yang didukung with
IEnumerable<T> IReadOnlyCollection<T> IReadOnlyList<T> () (kosong saja)
ICollection<T>, IList<T> (), (int capacity)

Untuk IList<T> dan ICollection<T>, pengkompilasi menggunakan System.Collections.Generic.List<T> dengan konstruktor yang ditentukan.

Pembatasan

Elemen with(...) memiliki batasan berikut:

  • Ini harus menjadi elemen pertama dalam ekspresi koleksi.
  • Argumen tidak dapat memiliki dynamic tipe.
  • Ini tidak didukung untuk jenis array atau rentang (Span<T>, ReadOnlySpan<T>).