Penginisialisasi Objek dan Koleksi (Panduan Pemrograman C#)

C# memungkinkan Anda membuat objek atau koleksi dan melakukan penugasan anggota dalam satu pernyataan.

Penginisialisasi objek

Penginisialisasi objek memungkinkan Anda menetapkan nilai ke bidang atau properti objek apa pun yang dapat diakses pada waktu pembuatan tanpa harus memanggil konstruktor diikuti dengan baris pernyataan penugasan. Sintaks penginisialisasi objek memungkinkan Anda menentukan argumen untuk konstruktor atau menghilangkan argumen (dan sintaks tanda kurung). Contoh berikut menunjukkan cara menggunakan penginisialisasi objek dengan jenis bernama, Cat dan cara memanggil konstruktor tanpa parameter. Perhatikan penggunaan properti yang diterapkan secara otomatis di kelas Cat. Untuk informasi selengkapnya, lihat Properti yang Diterapkan Secara Otomatis.

public class Cat
{
    // Auto-implemented properties.
    public int Age { get; set; }
    public string? Name { get; set; }

    public Cat()
    {
    }

    public Cat(string name)
    {
        this.Name = name;
    }
}
Cat cat = new Cat { Age = 10, Name = "Fluffy" };
Cat sameCat = new Cat("Fluffy"){ Age = 10 };

Sintaks penginisialisasi objek memungkinkan Anda membuat instans, dan setelah itu menetapkan objek yang baru dibuat, dengan properti yang ditetapkan, ke variabel dalam penugasan.

Penginisialisasi objek dapat mengatur pengindeks, selain menetapkan bidang dan properti. Pertimbangkan kelas Matrix dasar ini:

public class Matrix
{
    private double[,] storage = new double[3, 3];

    public double this[int row, int column]
    {
        // The embedded array will throw out of range exceptions as appropriate.
        get { return storage[row, column]; }
        set { storage[row, column] = value; }
    }
}

Anda dapat menginisialisasi matriks identitas dengan kode berikut:

var identity = new Matrix
{
    [0, 0] = 1.0,
    [0, 1] = 0.0,
    [0, 2] = 0.0,

    [1, 0] = 0.0,
    [1, 1] = 1.0,
    [1, 2] = 0.0,

    [2, 0] = 0.0,
    [2, 1] = 0.0,
    [2, 2] = 1.0,
};

Setiap pengindeks yang dapat diakses yang berisi pengatur yang dapat diakses dapat digunakan sebagai salah satu ekspresi dalam penginisialisasi objek, terlepas dari jumlah atau jenis argumen. Argumen indeks membentuk sisi kiri penugasan, dan nilainya adalah sisi kanan ekspresi. Misalnya, ini semua valid jika IndexersExample memiliki pengindeks yang sesuai:

var thing = new IndexersExample
{
    name = "object one",
    [1] = '1',
    [2] = '4',
    [3] = '9',
    Size = Math.PI,
    ['C',4] = "Middle C"
}

Agar kode sebelumnya dapat dikompilasi, jenis IndexersExample harus memiliki anggota berikut:

public string name;
public double Size { set { ... }; }
public char this[int i] { set { ... }; }
public string this[char c, int i] {  set { ... }; }

Penginisialisasi Objek dengan jenis anonim

Meskipun penginisialisasi objek dapat digunakan dalam konteks apa pun, penginisialisasi objek sangat berguna dalam ekspresi kueri LINQ. Ekspresi kueri sering menggunakan jenis anonim, yang hanya dapat diinisialisasi dengan menggunakan penginisialisasi objek, seperti yang ditunjukkan dalam deklarasi berikut.

var pet = new { Age = 10, Name = "Fluffy" };  

Jenis anonim mengaktifkan klausul select dalam ekspresi kueri LINQ untuk mengubah objek dari urutan asli menjadi objek yang nilai dan bentuknya mungkin berbeda dari aslinya. Hal ini berguna jika Anda ingin menyimpan hanya sebagian informasi dari setiap objek secara berurutan. Dalam contoh berikut, asumsikan bahwa objek produk (p) berisi banyak bidang dan metode, dan Anda hanya tertarik untuk membuat urutan objek yang berisi nama produk dan harga satuan.

var productInfos =
    from p in products
    select new { p.ProductName, p.UnitPrice };

Saat kueri ini dijalankan, variabel productInfos akan berisi urutan objek yang dapat diakses dalam pernyataan foreach seperti yang ditunjukkan dalam contoh ini:

foreach(var p in productInfos){...}  

Setiap objek dalam jenis anonim baru memiliki dua properti publik yang menerima nama yang sama sebagai properti atau bidang di objek asli. Anda juga dapat mengganti nama bidang saat Anda membuat jenis anonim; contoh berikut mengganti nama bidang UnitPrice menjadi Price.

select new {p.ProductName, Price = p.UnitPrice};  

Penginisialisasi Objek dengan pengubah required

Anda menggunakan required kata kunci untuk memaksa penelepon mengatur nilai properti atau bidang menggunakan penginisialisasi objek. Properti yang diperlukan tidak perlu ditetapkan sebagai parameter konstruktor. Pengkompilasi memastikan semua penelepon menginisialisasi nilai-nilai tersebut.

public class Pet
{
    public required int Age;
    public string Name;
}

// `Age` field is necessary to be initialized.
// You don't need to initialize `Name` property
var pet = new Pet() { Age = 10};

// Compiler error:
// Error CS9035 Required member 'Pet.Age' must be set in the object initializer or attribute constructor.
// var pet = new Pet();

Ini adalah praktik umum untuk menjamin bahwa objek Anda diinisialisasi dengan benar, terutama ketika Anda memiliki beberapa bidang atau properti untuk dikelola dan tidak ingin menyertakan semuanya dalam konstruktor.

Penginisialisasi Objek dengan init aksesor

Memastikan tidak ada yang mengubah objek yang dirancang dapat dibatasi dengan menggunakan init aksesor. Ini membantu membatasi pengaturan nilai properti.

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; init; }
}

// The `LastName` property can be set only during initialization. It CAN'T be modified afterwards.
// The `FirstName` property can be modified after initialization.
var pet = new Person() { FirstName = "Joe", LastName = "Doe"};

// You can assign the FirstName property to a different value.
pet.FirstName = "Jane";

// Compiler error:
// Error CS8852  Init - only property or indexer 'Person.LastName' can only be assigned in an object initializer,
//               or on 'this' or 'base' in an instance constructor or an 'init' accessor.
// pet.LastName = "Kowalski";

Properti khusus init yang diperlukan mendukung struktur yang tidak dapat diubah sambil memungkinkan sintaksis alami untuk pengguna jenis tersebut.

Penginisialisasi Objek dengan properti berjenis kelas

Saat menginisialisasi objek, terutama saat menggunakan kembali instans saat ini, sangat penting untuk mempertimbangkan implikasi untuk properti yang diketik kelas.

public class HowToClassTypedInitializer
{
    public class EmbeddedClassTypeA
    {
        public int I { get; set; }
        public bool B { get; set; }
        public string S { get; set; }
        public EmbeddedClassTypeB ClassB { get; set; }

        public override string ToString() => $"{I}|{B}|{S}|||{ClassB}";

        public EmbeddedClassTypeA()
        {
            Console.WriteLine($"Entering EmbeddedClassTypeA constructor. Values are: {this}");
            I = 3;
            B = true;
            S = "abc";
            ClassB = new() { BB = true, BI = 43 };
            Console.WriteLine($"Exiting EmbeddedClassTypeA constructor. Values are: {this})");
        }
    }

    public class EmbeddedClassTypeB
    {
        public int BI { get; set; }
        public bool BB { get; set; }
        public string BS { get; set; }

        public override string ToString() => $"{BI}|{BB}|{BS}";

        public EmbeddedClassTypeB()
        {
            Console.WriteLine($"Entering EmbeddedClassTypeB constructor. Values are: {this}");
            BI = 23;
            BB = false;
            BS = "BBBabc";
            Console.WriteLine($"Exiting EmbeddedClassTypeB constructor. Values are: {this})");
        }
    }

    public static void Main()
    {
        var a = new EmbeddedClassTypeA
        {
            I = 103,
            B = false,
            ClassB = { BI = 100003 }
        };
        Console.WriteLine($"After initializing EmbeddedClassTypeA: {a}");

        var a2 = new EmbeddedClassTypeA
        {
            I = 103,
            B = false,
            ClassB = new() { BI = 100003 } //New instance
        };
        Console.WriteLine($"After initializing EmbeddedClassTypeA a2: {a2}");
    }

    // Output:
    //Entering EmbeddedClassTypeA constructor Values are: 0|False||||
    //Entering EmbeddedClassTypeB constructor Values are: 0|False|
    //Exiting EmbeddedClassTypeB constructor Values are: 23|False|BBBabc)
    //Exiting EmbeddedClassTypeA constructor Values are: 3|True|abc|||43|True|BBBabc)
    //After initializing EmbeddedClassTypeA: 103|False|abc|||100003|True|BBBabc
    //Entering EmbeddedClassTypeA constructor Values are: 0|False||||
    //Entering EmbeddedClassTypeB constructor Values are: 0|False|
    //Exiting EmbeddedClassTypeB constructor Values are: 23|False|BBBabc)
    //Exiting EmbeddedClassTypeA constructor Values are: 3|True|abc|||43|True|BBBabc)
    //Entering EmbeddedClassTypeB constructor Values are: 0|False|
    //Exiting EmbeddedClassTypeB constructor Values are: 23|False|BBBabc)
    //After initializing EmbeddedClassTypeA a2: 103|False|abc|||100003|False|BBBabc
}

Contoh berikut menunjukkan bagaimana, untuk ClassB, proses inisialisasi melibatkan pembaruan nilai tertentu sambil mempertahankan orang lain dari instans asli. Initializer menggunakan kembali instans saat ini: Nilai ClassB adalah: 100003 (nilai baru yang kami tetapkan di sini), true (disimpan dari inisialisasi EmbeddedClassTypeA), BBBabc (default yang tidak berubah dari EmbeddedClassTypeB)

Penginisialisasi koleksi

Penginisialisasi koleksi memungkinkan Anda menentukan satu atau beberapa penginisialisasi elemen saat Anda menginisialisasi jenis koleksi yang menerapkan IEnumerable dan memiliki Add dengan tanda tangan yang sesuai sebagai metode instans atau metode ekstensi. Penginisialisasi elemen dapat berupa nilai sederhana, ekspresi, atau penginisialisasi objek. Dengan menggunakan penginisialisasi koleksi, Anda tidak perlu menentukan beberapa panggilan; kompilator menambahkan panggilan secara otomatis.

Contoh berikut menunjukkan dua penginisialisasi koleksi sederhana:

List<int> digits = new List<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };  
List<int> digits2 = new List<int> { 0 + 1, 12 % 3, MakeInt() };  

Penginisialisasi koleksi berikut menggunakan penginisialisasi objek untuk menginisialisasi objek dari kelas Cat yang ditentukan dalam contoh sebelumnya. Perhatikan bahwa setiap penginisialisasi objek diapit dalam kurung kurawal dan dipisahkan dengan koma.

List<Cat> cats = new List<Cat>
{
    new Cat{ Name = "Sylvester", Age=8 },
    new Cat{ Name = "Whiskers", Age=2 },
    new Cat{ Name = "Sasha", Age=14 }
};

Anda dapat menentukan null sebagai elemen dalam penginisialisasi koleksi jika metode Add koleksi mengizinkannya.

List<Cat?> moreCats = new List<Cat?>
{
    new Cat{ Name = "Furrytail", Age=5 },
    new Cat{ Name = "Peaches", Age=4 },
    null
};

Anda dapat menentukan elemen yang diindeks jika koleksi mendukung pengindeksan baca / tulis.

var numbers = new Dictionary<int, string>
{
    [7] = "seven",
    [9] = "nine",
    [13] = "thirteen"
};

Sampel sebelumnya menghasilkan kode yang memanggil Item[TKey] untuk mengatur nilai. Anda juga dapat menginisialisasi kamus dan kontainer asosiatif lainnya menggunakan sintaks berikut. Perhatikan bahwa sintaks menggunakan objek dengan beberapa nilai, bukan sintaks pengindeks, dengan tanda kurung dan penugasan:

var moreNumbers = new Dictionary<int, string>
{
    {19, "nineteen" },
    {23, "twenty-three" },
    {42, "forty-two" }
};

Contoh penginisialisasi ini memanggil Add(TKey, TValue) untuk menambahkan tiga item ke dalam kamus. Dua cara berbeda untuk menginisialisasi koleksi asosiatif ini memiliki perilaku yang sedikit berbeda karena metode yang dihasilkan oleh pemanggilan kompilator. Kedua varian berfungsi dengan kelas Dictionary. Jenis lain mungkin hanya mendukung satu atau yang lain berdasarkan API publik jenis.

Penginisialisasi Objek dengan inisialisasi properti baca-saja koleksi

Beberapa kelas mungkin memiliki properti koleksi yang propertinya merupakan baca-saja, seperti properti Cats dari CatOwner dalam kasus berikut:

public class CatOwner
{
    public IList<Cat> Cats { get; } = new List<Cat>();
}

Anda tidak akan dapat menggunakan sintaks penginisialisasi koleksi yang dibahas sejauh ini karena properti tidak dapat diberi daftar baru:

CatOwner owner = new CatOwner
{
    Cats = new List<Cat>
    {
        new Cat{ Name = "Sylvester", Age=8 },
        new Cat{ Name = "Whiskers", Age=2 },
        new Cat{ Name = "Sasha", Age=14 }
    }
};

Tetapi, entri baru dapat ditambahkan ke Cats tetap menggunakan sintaks inisialisasi dengan menghilangkan pembuatan daftar (new List<Cat>), seperti yang ditunjukkan berikutnya:

CatOwner owner = new CatOwner
{
    Cats =
    {
        new Cat{ Name = "Sylvester", Age=8 },
        new Cat{ Name = "Whiskers", Age=2 },
        new Cat{ Name = "Sasha", Age=14 }
    }
};

Kumpulan entri yang akan ditambahkan hanya tampak dikelilingi oleh kurung kurawal. Di atas identik dengan menulis:

CatOwner owner = new CatOwner();
owner.Cats.Add(new Cat{ Name = "Sylvester", Age=8 });
owner.Cats.Add(new Cat{ Name = "Whiskers", Age=2 });
owner.Cats.Add(new Cat{ Name = "Sasha", Age=14 });

Contoh

Contoh berikut menggabungkan konsep penginisialisasi objek dan koleksi.

public class InitializationSample
{
    public class Cat
    {
        // Auto-implemented properties.
        public int Age { get; set; }
        public string? Name { get; set; }

        public Cat() { }

        public Cat(string name)
        {
            Name = name;
        }
    }

    public static void Main()
    {
        Cat cat = new Cat { Age = 10, Name = "Fluffy" };
        Cat sameCat = new Cat("Fluffy"){ Age = 10 };

        List<Cat> cats = new List<Cat>
        {
            new Cat { Name = "Sylvester", Age = 8 },
            new Cat { Name = "Whiskers", Age = 2 },
            new Cat { Name = "Sasha", Age = 14 }
        };

        List<Cat?> moreCats = new List<Cat?>
        {
            new Cat { Name = "Furrytail", Age = 5 },
            new Cat { Name = "Peaches", Age = 4 },
            null
        };

        // Display results.
        System.Console.WriteLine(cat.Name);

        foreach (Cat c in cats)
            System.Console.WriteLine(c.Name);

        foreach (Cat? c in moreCats)
            if (c != null)
                System.Console.WriteLine(c.Name);
            else
                System.Console.WriteLine("List element has null value.");
    }
    // Output:
    //Fluffy
    //Sylvester
    //Whiskers
    //Sasha
    //Furrytail
    //Peaches
    //List element has null value.
}

Contoh berikut menunjukkan objek yang menerapkan IEnumerable dan berisi metode Add dengan beberapa parameter, Contoh berikut menggunakan penginisialisasi koleksi dengan beberapa elemen per item dalam daftar yang sesuai dengan tanda tangan metode Add.

    public class FullExample
    {
        class FormattedAddresses : IEnumerable<string>
        {
            private List<string> internalList = new List<string>();
            public IEnumerator<string> GetEnumerator() => internalList.GetEnumerator();

            System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => internalList.GetEnumerator();

            public void Add(string firstname, string lastname,
                string street, string city,
                string state, string zipcode) => internalList.Add(
                $@"{firstname} {lastname}
{street}
{city}, {state} {zipcode}"
                );
        }

        public static void Main()
        {
            FormattedAddresses addresses = new FormattedAddresses()
            {
                {"John", "Doe", "123 Street", "Topeka", "KS", "00000" },
                {"Jane", "Smith", "456 Street", "Topeka", "KS", "00000" }
            };

            Console.WriteLine("Address Entries:");

            foreach (string addressEntry in addresses)
            {
                Console.WriteLine("\r\n" + addressEntry);
            }
        }

        /*
         * Prints:

            Address Entries:

            John Doe
            123 Street
            Topeka, KS 00000

            Jane Smith
            456 Street
            Topeka, KS 00000
         */
    }

Metode Add dapat menggunakan kata kunci params untuk mengambil beberapa argumen variabel, seperti yang ditunjukkan pada contoh berikut. Contoh ini juga menunjukkan penerapan kustom pengindeks untuk menginisialisasi koleksi menggunakan indeks.

public class DictionaryExample
{
    class RudimentaryMultiValuedDictionary<TKey, TValue> : IEnumerable<KeyValuePair<TKey, List<TValue>>> where TKey : notnull
    {
        private Dictionary<TKey, List<TValue>> internalDictionary = new Dictionary<TKey, List<TValue>>();

        public IEnumerator<KeyValuePair<TKey, List<TValue>>> GetEnumerator() => internalDictionary.GetEnumerator();

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => internalDictionary.GetEnumerator();

        public List<TValue> this[TKey key]
        {
            get => internalDictionary[key];
            set => Add(key, value);
        }

        public void Add(TKey key, params TValue[] values) => Add(key, (IEnumerable<TValue>)values);

        public void Add(TKey key, IEnumerable<TValue> values)
        {
            if (!internalDictionary.TryGetValue(key, out List<TValue>? storedValues))
                internalDictionary.Add(key, storedValues = new List<TValue>());

            storedValues.AddRange(values);
        }
    }

    public static void Main()
    {
        RudimentaryMultiValuedDictionary<string, string> rudimentaryMultiValuedDictionary1
            = new RudimentaryMultiValuedDictionary<string, string>()
            {
                {"Group1", "Bob", "John", "Mary" },
                {"Group2", "Eric", "Emily", "Debbie", "Jesse" }
            };
        RudimentaryMultiValuedDictionary<string, string> rudimentaryMultiValuedDictionary2
            = new RudimentaryMultiValuedDictionary<string, string>()
            {
                ["Group1"] = new List<string>() { "Bob", "John", "Mary" },
                ["Group2"] = new List<string>() { "Eric", "Emily", "Debbie", "Jesse" }
            };
        RudimentaryMultiValuedDictionary<string, string> rudimentaryMultiValuedDictionary3
            = new RudimentaryMultiValuedDictionary<string, string>()
            {
                {"Group1", new string []{ "Bob", "John", "Mary" } },
                { "Group2", new string[]{ "Eric", "Emily", "Debbie", "Jesse" } }
            };

        Console.WriteLine("Using first multi-valued dictionary created with a collection initializer:");

        foreach (KeyValuePair<string, List<string>> group in rudimentaryMultiValuedDictionary1)
        {
            Console.WriteLine($"\r\nMembers of group {group.Key}: ");

            foreach (string member in group.Value)
            {
                Console.WriteLine(member);
            }
        }

        Console.WriteLine("\r\nUsing second multi-valued dictionary created with a collection initializer using indexing:");

        foreach (KeyValuePair<string, List<string>> group in rudimentaryMultiValuedDictionary2)
        {
            Console.WriteLine($"\r\nMembers of group {group.Key}: ");

            foreach (string member in group.Value)
            {
                Console.WriteLine(member);
            }
        }
        Console.WriteLine("\r\nUsing third multi-valued dictionary created with a collection initializer using indexing:");

        foreach (KeyValuePair<string, List<string>> group in rudimentaryMultiValuedDictionary3)
        {
            Console.WriteLine($"\r\nMembers of group {group.Key}: ");

            foreach (string member in group.Value)
            {
                Console.WriteLine(member);
            }
        }
    }

    /*
     * Prints:

        Using first multi-valued dictionary created with a collection initializer:

        Members of group Group1:
        Bob
        John
        Mary

        Members of group Group2:
        Eric
        Emily
        Debbie
        Jesse

        Using second multi-valued dictionary created with a collection initializer using indexing:

        Members of group Group1:
        Bob
        John
        Mary

        Members of group Group2:
        Eric
        Emily
        Debbie
        Jesse

        Using third multi-valued dictionary created with a collection initializer using indexing:

        Members of group Group1:
        Bob
        John
        Mary

        Members of group Group2:
        Eric
        Emily
        Debbie
        Jesse
     */
}

Lihat juga