Bagikan melalui


Ekspresi with - Mutasi nondestruktif membuat objek baru dengan properti yang dimodifikasi

Ekspresi with membuat salinan operand-nya dengan properti dan bidang yang ditentukan dimodifikasi. Gunakan sintaks penginisialisasi objek untuk menentukan anggota mana yang akan dimodifikasi dan nilai barunya:

using System;

public class WithExpressionBasicExample
{
    public record NamedPoint(string Name, int X, int Y);

    public static void Main()
    {
        var p1 = new NamedPoint("A", 0, 0);
        Console.WriteLine($"{nameof(p1)}: {p1}");  // output: p1: NamedPoint { Name = A, X = 0, Y = 0 }
        
        var p2 = p1 with { Name = "B", X = 5 };
        Console.WriteLine($"{nameof(p2)}: {p2}");  // output: p2: NamedPoint { Name = B, X = 5, Y = 0 }
        
        var p3 = p1 with 
            { 
                Name = "C", 
                Y = 4 
            };
        Console.WriteLine($"{nameof(p3)}: {p3}");  // output: p3: NamedPoint { Name = C, X = 0, Y = 4 }

        Console.WriteLine($"{nameof(p1)}: {p1}");  // output: p1: NamedPoint { Name = A, X = 0, Y = 0 }

        var apples = new { Item = "Apples", Price = 1.19m };
        Console.WriteLine($"Original: {apples}");  // output: Original: { Item = Apples, Price = 1.19 }
        var saleApples = apples with { Price = 0.79m };
        Console.WriteLine($"Sale: {saleApples}");  // output: Sale: { Item = Apples, Price = 0.79 }
    }
}

Operan with sebelah kiri ekspresi bisa menjadi tipe rekaman. Ini juga bisa menjadi jenis struktur atau jenis anonim.

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#.

Hasil ekspresi with memiliki jenis run-time yang sama dengan operand ekspresi, seperti yang ditunjukkan contoh berikut:

using System;

public class InheritanceExample
{
    public record Point(int X, int Y);
    public record NamedPoint(string Name, int X, int Y) : Point(X, Y);

    public static void Main()
    {
        Point p1 = new NamedPoint("A", 0, 0);
        Point p2 = p1 with { X = 5, Y = 3 };
        Console.WriteLine(p2 is NamedPoint);  // output: True
        Console.WriteLine(p2);  // output: NamedPoint { X = 5, Y = 3, Name = A }
    }
}

Saat anggota adalah jenis referensi, menyalin anggota tersebut hanya menyalin referensi ke instans anggota tersebut. Salinan dan operand asli mengakses instans jenis referensi yang sama. Contoh berikut menunjukkan perilaku tersebut:

using System;
using System.Collections.Generic;

public class ExampleWithReferenceType
{
    public record TaggedNumber(int Number, List<string> Tags)
    {
        public string PrintTags() => string.Join(", ", Tags);
    }

    public static void Main()
    {
        var original = new TaggedNumber(1, new List<string> { "A", "B" });

        var copy = original with { Number = 2 };
        Console.WriteLine($"Tags of {nameof(copy)}: {copy.PrintTags()}");
        // output: Tags of copy: A, B

        original.Tags.Add("C");
        Console.WriteLine($"Tags of {nameof(copy)}: {copy.PrintTags()}");
        // output: Tags of copy: A, B, C
    }
}

Semantik salinan kustom

Setiap jenis kelas rekaman memiliki konstruktor salinan. Konstruktor copy adalah konstruktor dengan parameter tunggal dari tipe record yang mengandung. Ini menyalin keadaan argumennya ke dalam sebuah instans rekaman baru. Saat Anda mengevaluasi with ekspresi, ekspresi memanggil konstruktor salin untuk membuat instans rekaman baru berdasarkan rekaman asli. Kemudian, ini memperbarui instans baru dengan modifikasi yang ditentukan. Secara default, kompilator mensintesis konstruktor salinan. Untuk menyesuaikan semantik salinan rekaman, deklarasikan konstruktor salinan secara eksplisit dengan perilaku yang diinginkan. Contoh berikut memperbarui contoh sebelumnya dengan konstruktor salinan eksplisit. Perilaku salin baru menyalin item daftar alih-alih referensi daftar saat rekaman disalin:

using System;
using System.Collections.Generic;

public class UserDefinedCopyConstructorExample
{
    public record TaggedNumber(int Number, List<string> Tags)
    {
        protected TaggedNumber(TaggedNumber original)
        {
            Number = original.Number;
            Tags = new List<string>(original.Tags);
        }

        public string PrintTags() => string.Join(", ", Tags);
    }

    public static void Main()
    {
        var original = new TaggedNumber(1, new List<string> { "A", "B" });

        var copy = original with { Number = 2 };
        Console.WriteLine($"Tags of {nameof(copy)}: {copy.PrintTags()}");
        // output: Tags of copy: A, B

        original.Tags.Add("C");
        Console.WriteLine($"Tags of {nameof(copy)}: {copy.PrintTags()}");
        // output: Tags of copy: A, B
    }
}

Anda tidak dapat menyesuaikan semantik penyalinan untuk tipe struktur.

Penting

Dalam contoh sebelumnya, semua properti independen. Tidak ada properti yang dihitung dari nilai properti lainnya. Ekspresi with terlebih dahulu menyalin instans rekaman yang ada, lalu memodifikasi properti atau bidang apa pun yang ditentukan dalam with ekspresi. Properti komputasi dalam record jenis harus dihitung pada akses, bukan diinisialisasi saat instans dibuat. Jika tidak, properti mungkin mengembalikan nilai komputasi berdasarkan instans asli, bukan salinan yang dimodifikasi. Untuk informasi selengkapnya, lihat artikel referensi bahasa tentang record jenis.

Spesifikasi bahasa C#

Untuk informasi selengkapnya, lihat bagian berikut dari catatan proposal fiturdi catatan :

Lihat juga