dengan ekspresi - Mutasi nondestruktif membuat objek baru dengan properti yang dimodifikasi

Ekspresi with menghasilkan salinan operand-nya dengan properti dan bidang yang ditentukan yang dimodifikasi. Anda menggunakan sintaksis penginisialisasi objek untuk menentukan anggota apa yang akan diubah 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 dari jenis rekaman. Dimulai dengan C# 10, operand kiri dari ekspresi with juga bisa dari jenis struktur atau jenis anonim.

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

Dalam kasus anggota jenis referensi, hanya referensi ke instans anggota yang disalin saat operand disalin. Salinan dan operand asli memiliki akses ke 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

Jenis kelas rekaman apa pun memiliki konstruktor salinan. Konstruktor salinan adalah konstruktor dengan parameter tunggal dari jenis rekaman yang berisi. Ini menyalin status argumennya ke instans rekaman baru. Pada evaluasi ekspresi with, konstruktor salinan dipanggil untuk membuat instans rekaman baru berdasarkan rekaman asli. Setelah itu, instans baru akan diperbarui sesuai dengan modifikasi yang ditentukan. Secara default, konstruktor salinan bersifat implisit, yaitu, yang dihasilkan kompilator. Jika Anda perlu 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 adalah 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 salinan untuk jenis struktur.

Spesifikasi bahasa C#

Untuk informasi selengkapnya, lihat bagian berikut dari catatan proposal fitur rekaman:

Lihat juga