with – výraz (Referenční příručka jazyka C#)

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

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 objektu k určení členů, které se mají upravit, a jejich nových hodnot.

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 výrazu také typu struktury with nebo anonymního typu.

Výsledek výrazu má stejný typ běhu jako operand výrazu, jak ukazuje with 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 je zkopírován pouze odkaz na instanci členu při kopírování operandu. Kopie i původní operand mají přístup ke stejné instanci typu odkazu. Toto chování je znázorněno v následujícím příkladu:

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í

Každý typ třídy záznamu má kopírovací konstruktor. To je konstruktor s jedním parametrem obsahujícího typu záznamu. Zkopíruje stav svého argumentu do nové instance záznamu. Při vyhodnocení výrazu se volá kopírovací konstruktor pro vytvoření instance nového záznamu with 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í, to znamená generovaný kompilátorem. Pokud potřebujete přizpůsobit sémantiku kopírování záznamů, 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í při kopírování je zkopí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émantiku kopírování nelze přizpůsobit pro typy struktur.

specifikace jazyka C#

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

Viz také