with expression - Nondestructive mut creates a new object with modified properties

Výraz with vytvoří kopii svého operandu se zadanými vlastnostmi a upravenými poli. Pomocí syntaxe inicializátoru objektů můžete určit, které členy se mají upravit, a jejich nové hodnoty:

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

Levý operand výrazu with může být typu záznamu. Počínaje jazykem C# 10 může být levý operand with výrazu také typustruktury nebo anonymního typu.

Výsledek výrazu with má stejný typ za běhu jako operand výrazu, jak ukazuje následující příklad:

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

V případě členu typu odkazu se při kopírování operandu zkopíruje pouze odkaz na instanci člena. Kopie i původní operand mají přístup ke stejné instanci typu odkazu. Následující příklad ukazuje toto chování:

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

Vlastní sémantika kopírování

Jakýkoli typ třídy záznamu má konstruktor kopírování. Konstruktor kopírování je konstruktor s jedním parametrem obsahujícího typ záznamu. Zkopíruje stav svého argumentu do nové instance záznamu. Při vyhodnocení výrazu with se konstruktor kopírování zavolá k vytvoření instance nového záznamu na základě původního záznamu. Potom se nová instance aktualizuje podle zadaných úprav. Ve výchozím nastavení je konstruktor kopírování implicitní, tj. kompilátor generovaný. Pokud potřebujete přizpůsobit sémantiku kopírování záznamu, explicitně deklarujte konstruktor kopírování s požadovaným chováním. Následující příklad aktualizuje předchozí příklad pomocí explicitního konstruktoru kopírování. Nové chování kopírování spočívá ve kopírování položek seznamu místo odkazu na seznam při kopírování záznamu:

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

Sémantika kopírování pro typy struktur se nedá přizpůsobit.

specifikace jazyka C#

Další informace najdete v následujících částech návrhu funkce záznamů:

Viz také