s výrazem (referenční dokumentace jazyka C#)

Dostupný v jazyce C# 9.0 a novějším with vytvoří výraz kopii svého operandu se zadanými vlastnostmi a poli upravenými:

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.19" };
        Console.WriteLine($"original apples: {apples}");
        var saleApples = apples with { Price = "0.79" };
        Console.WriteLine($"sale apples: {saleApples}");

    }
}

Jak ukazuje předchozí příklad, použijete syntaxi inicializátoru objektů k určení členů, které se mají upravit, a jejich nové hodnoty.

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

Výsledek výrazu with má stejný typ 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ě člena typu odkazu se při zkopírování operandu zkopíruje pouze odkaz na instanci člena. Kopie i původní operand mají přístup ke stejné instanci referenčního typu. 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áznamů má konstruktor kopírování. Jedná se o konstruktor s jedním parametrem obsahujícího typ záznamu. Zkopíruje stav 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 vygenerovaný. 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 explicitním konstruktorem kopírování. Nové chování kopírování spočívá v kopírování položek seznamu místo odkazu na seznam při zkopí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émantiku kopírování pro typy struktur nelze přizpůsobit.

specifikace jazyka C#

Další informace najdete v následujících částech poznámky k návrhu funkcí záznamů:

Viz také