Membuat instans objek menggunakan penginisialisasi dan menyalin konstruktor

Selesai

Penginisialisasi objek dan konstruktor salin adalah dua cara untuk membuat instans objek di C#. Penginisialisasi objek memungkinkan Anda menetapkan nilai ke bidang atau properti objek yang dapat diakses pada waktu pembuatan tanpa harus memanggil konstruktor diikuti oleh baris pernyataan penugasan. Menyalin konstruktor memungkinkan Anda membuat objek baru dengan menyalin nilai objek yang sudah ada.

Menggunakan penginisialisasi objek dan konstruktor salinan dapat membantu Anda menulis kode yang lebih ringkas dan dapat dibaca.

Penginisialisasi objek

Penginisialisasi objek memungkinkan Anda menetapkan nilai ke bidang atau properti objek yang dapat diakses pada waktu pembuatan tanpa harus memanggil konstruktor diikuti oleh baris pernyataan penugasan. Sintaks penginisialisasi objek memungkinkan Anda menentukan argumen untuk konstruktor atau menghilangkan argumen (dan sintaks tanda kurung).

Anda dapat menggunakan penginisialisasi objek untuk menginisialisasi objek jenis secara deklaratif tanpa secara eksplisit memanggil konstruktor untuk jenis tersebut.

Pengkompilasi memproses penginisialisasi objek dengan terlebih dahulu mengakses konstruktor instans tanpa parameter lalu memproses inisialisasi anggota. Oleh karena itu, jika konstruktor tanpa parameter dinyatakan sebagai privat di kelas , penginisialisasi objek yang memerlukan akses publik akan gagal.

Contoh berikut menunjukkan cara menggunakan penginisialisasi objek.

Sampel kode pertama menunjukkan definisi kelas untuk kelas bernama Cat. Definisi ini mencakup dua konstruktor, salah satunya adalah konstruktor tanpa parameter. Sampel kode kedua menunjukkan cara membuat instans objek Cat menggunakan penginisialisasi objek. Penginisialisasi objek menetapkan nilai ke properti Age dan Name objek Cat.


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

    public Cat()
    {
    }

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


public class Program
{
    public static void Main()
    {
        // Declare and instantiate a new Cat object by using an object initializer.
        Cat cat = new Cat { Age = 10, Name = "Fluffy" };

        // Declare and instantiate a new Cat object by using an object initializer.
        Cat sameCat = new Cat("Fluffy") { Age = 10 };
    }
}

Berikut adalah contoh lain yang menunjukkan cara menginisialisasi jenis StudentName baru dengan menggunakan penginisialisasi objek. Contoh ini mengatur properti dalam jenis StudentName:


public class HowToObjectInitializers
{
    public static void Main()
    {
        // Declare a StudentName by using the constructor that has two parameters.
        StudentName student1 = new StudentName("Lisa", "Yeh");

        // Make the same declaration by using an object initializer and sending
        // arguments for the first and last names. The parameterless constructor is
        // invoked in processing this declaration, not the constructor that has
        // two parameters.
        StudentName student2 = new StudentName
        {
            FirstName = "Sandy",
            LastName = "Zoeng"
        };

        // Declare a StudentName by using an object initializer and sending
        // an argument for only the ID property. No corresponding constructor is
        // necessary. Only the parameterless constructor is used to process object
        // initializers.
        StudentName student3 = new StudentName
        {
            ID = 183
        };

        // Declare a StudentName by using an object initializer and sending
        // arguments for all three properties. No corresponding constructor is
        // defined in the class.
        StudentName student4 = new StudentName
        {
            FirstName = "Thomas",
            LastName = "Margand",
            ID = 116
        };

        Console.WriteLine(student1.ToString());
        Console.WriteLine(student2.ToString());
        Console.WriteLine(student3.ToString());
        Console.WriteLine(student4.ToString());
    }
    // Output:
    // Lisa  0
    // Sandy  0
    //   183
    // Thomas  116

    public class StudentName
    {
        // This constructor has no parameters. The parameterless constructor
        // is invoked in the processing of object initializers.
        // You can test this by changing the access modifier from public to
        // private. The declarations in Main that use object initializers will
        // fail.
        public StudentName() { }

        // The following constructor has parameters for two of the three
        // properties.
        public StudentName(string first, string last)
        {
            FirstName = first;
            LastName = last;
        }

        // Properties.
        public string? FirstName { get; set; }
        public string? LastName { get; set; }
        public int ID { get; set; }

        // Override the ToString method of the Object class.
        public override string ToString() => FirstName + "  " + ID;
    }
}

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

Selain menetapkan bidang dan properti, penginisialisasi objek dapat mengatur pengindeks. 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,
};

Contoh berikut mendefinisikan kelas BaseballTeam yang menggunakan pengindeks untuk mendapatkan dan mengatur pemain pada posisi yang berbeda. Penginisialisasi dapat menetapkan pemain, berdasarkan singkatan untuk posisi tersebut, atau nomor yang digunakan untuk setiap posisi pada kartu skor bisbol:


public class HowToIndexInitializer
{
    public class BaseballTeam
    {
        private string[] players = new string[9];
        private readonly List<string> positionAbbreviations = new List<string>
        {
            "P", "C", "1B", "2B", "3B", "SS", "LF", "CF", "RF"
        };

        public string this[int position]
        {
            // Baseball positions are 1 - 9.
            get { return players[position-1]; }
            set { players[position-1] = value; }
        }
        public string this[string position]
        {
            get { return players[positionAbbreviations.IndexOf(position)]; }
            set { players[positionAbbreviations.IndexOf(position)] = value; }
        }
    }

    public static void Main()
    {
        var team = new BaseballTeam
        {
            ["RF"] = "Lisa Yeh",
            [4] = "Sandy Zoeng",
            ["CF"] = "Thomas Margand"
        };

        Console.WriteLine(team["2B"]);
    }
}

Penginisialisasi objek dengan pengubah yang diperlukan

Anda menggunakan kata kunci yang diperlukan 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 Person
{
    public string FirstName { get; set; } = string.Empty;
    public required string LastName { get; set; }

}

// The `FirstName` property is optional and has a default value of an empty string.        
// The `LastName` property is required and must be initialized during object creation.
// You can create a new instance of the Person class using both properties.
var friend1 = new Person() { FirstName = "Lisa", LastName = "Yeh" };
Console.WriteLine($"Hello, {friend1.FirstName} {friend1.LastName}!");

// You can create a new instance of the Person class using only the LastName property.
var friend2 = new Person() { LastName = "Yeh"};
Console.WriteLine($"Hello, {friend2.FirstName} {friend2.LastName}!");

// You can assign a different value to the properties after the object is created.
friend2.FirstName = "Sandy";
friend2.LastName = "Chen";
Console.WriteLine($"Hello, {friend2.FirstName} {friend2.LastName}!");

// Compiler error: Required property 'LastName' must be initialized.
//var friend3 = new Person() { FirstName = "Lisa"};
//var friend4 = new Person();

// Output:
// Hello, Lisa Yeh!
// Hello, Sandy Yeh!
// Hello,  Yeh!
// Hello,  Chen!

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 aksesor init

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


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

// You can create a new instance of the Person class any combination of the properties.
var friend0 = new Person();
var friend1 = new Person() { FirstName = "Lisa" };
var friend2 = new Person() { LastName = "Yeh" };
var friend3 = new Person() { FirstName = "Lisa", LastName = "Yeh" };

Console.WriteLine($"Hello, {friend0.FirstName} {friend0.LastName}!");
Console.WriteLine($"Hello, {friend1.FirstName} {friend1.LastName}!");
Console.WriteLine($"Hello, {friend2.FirstName} {friend2.LastName}!");
Console.WriteLine($"Hello, {friend3.FirstName} {friend3.LastName}!");

// You can assign a different value to the FirstName property after the object is created, but not the LastName property.
friend3.FirstName = "Sandy";

// 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.
//friend3.LastName = "Chen";

Console.WriteLine($"Hello, {friend3.FirstName} {friend3.LastName}!");


// Output:
// Hello,  unknown!
// Hello, Lisa unknown!
// Hello,  Yeh!
// Hello, Lisa Yeh!
// Hello, Sandy Yeh!

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

Sangat penting untuk mempertimbangkan implikasi untuk properti yang diketik kelas saat menginisialisasi objek:


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 berikutnya menunjukkan urutan eksekusi konstruktor dan inisialisasi anggota menggunakan konstruktor dengan dan tanpa parameter:


public class ObjectInitializersExecutionOrder
{
    public static void Main()
    {
        new Person { FirstName = "Lisa", LastName = "Yeh", City = "unknown" };
        new Dog(2) { Name = "Oscar" };
    }

    public class Dog
    {
        private int age;
        private string name;

        public Dog(int age)
        {
            Console.WriteLine("Hello from Dog's non-parameterless constructor");
            this.age = age;
        }

        public required string Name
        {
            get { return name; }

            set
            {
                Console.WriteLine("Hello from setter of Dog's required property 'Name'");
                name = value;
            }
        }
    }

    public class Person
    {
        private string firstName;
        private string lastName;
        private string city;

        public Person()
        {
            Console.WriteLine("Hello from Person's parameterless constructor");
        }

        public required string FirstName
        {
            get { return firstName; }

            set
            {
                Console.WriteLine("Hello from setter of Person's required property 'FirstName'");
                firstName = value;
            }
        }

        public string LastName
        {
            get { return lastName; }

            init
            {
                Console.WriteLine("Hello from setter of Person's init property 'LastName'");
                lastName = value;
            }
        }

        public string City
        {
            get { return city; }

            set
            {
                Console.WriteLine("Hello from setter of Person's property 'City'");
                city = value;
            }
        }
    }

    // Output:
    // Hello from Person's parameterless constructor
    // Hello from setter of Person's required property 'FirstName'
    // Hello from setter of Person's init property 'LastName'
    // Hello from setter of Person's property 'City'
    // Hello from Dog's non-parameterless constructor
    // Hello from setter of Dog's required property 'Name'
}

Menyalin konstruktor

Dalam contoh berikut, kelas Person mendefinisikan konstruktor salinan yang mengambil, sebagai argumennya, instans Person. Nilai properti argumen ditetapkan ke properti instans baru Person. Kode berisi konstruktor salinan alternatif yang mengirim properti Name dan Age instans yang ingin Anda salin ke konstruktor instans kelas. Kelas Person disegel, sehingga tidak ada jenis turunan yang dapat dinyatakan yang dapat menimbulkan kesalahan dengan hanya menyalin kelas dasar.


public sealed class Person
{
    // Copy constructor.
    public Person(Person previousPerson)
    {
        Name = previousPerson.Name;
        Age = previousPerson.Age;
    }

    //// Alternate copy constructor calls the instance constructor.
    //public Person(Person previousPerson)
    //    : this(previousPerson.Name, previousPerson.Age)
    //{
    //}

    // Instance constructor.
    public Person(string name, int age)
    {
        Name = name;
        Age = age;
    }

    public int Age { get; set; }

    public string Name { get; set; }

    public string Details()
    {
        return Name + " is " + Age.ToString();
    }
}

class TestPerson
{
    static void Main()
    {
        // Create a Person object by using the instance constructor.
        Person person1 = new Person("Lisa", 40);

        // Create another Person object, copying person1.
        Person person2 = new Person(person1);

        // Change each person's age.
        person1.Age = 39;
        person2.Age = 41;

        // Change person2's name.
        person2.Name = "Sandy";

        // Show details to verify that the name and age fields are distinct.
        Console.WriteLine(person1.Details());
        Console.WriteLine(person2.Details());

        // Keep the console window open in debug mode.
        Console.WriteLine("Press any key to exit.");
        Console.ReadKey();
    }
}
// Output:
// Lisa is 39
// Sandy is 41