Dela via


Uttrycket with – icke-förstörande mutation skapar ett nytt objekt med ändrade egenskaper

Ett with uttryck skapar en kopia av operand med de angivna egenskaperna och fälten ändrade. Använd syntaxen för objektinitieraren för att ange vilka medlemmar som ska ändras och deras nya värden:

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

Den vänstra operanden för ett with uttryck kan vara en posttyp. Det kan också vara en strukturtyp eller en anonym typ.

C#-språkreferensen dokumenterar den senaste versionen av C#-språket. Den innehåller även inledande dokumentation för funktioner i offentliga förhandsversioner för den kommande språkversionen.

Dokumentationen identifierar alla funktioner som först introducerades i de tre senaste versionerna av språket eller i aktuella offentliga förhandsversioner.

Tips/Råd

Information om när en funktion först introducerades i C# finns i artikeln om språkversionshistoriken för C#.

Resultatet av ett with-uttryck har samma körningstidstyp som uttryckets operand, som följande exempel visar:

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

När en medlem är en referenstyp kopierar du endast referensen till den medlemsinstansen genom att kopiera den medlemmen. Både kopian och den ursprungliga operanden har åtkomst till samma referenstypsinstans. Följande exempel visar det beteendet:

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

Anpassad kopieringssemantik

Varje postklasstyp har en kopieringskonstruktor. En kopieringskonstruktor är en konstruktor med en enda parameter av den innehållande rekordtypen. Det kopierar tillståndet av sitt argument till en ny objektinstans. När du utvärderar ett with uttryck anropas kopieringskonstruktorn för att skapa en ny postinstans baserat på en ursprunglig post. Sedan uppdateras den nya instansen med de angivna ändringarna. Som standard syntetiserar kompilatorn kopieringskonstruktorn. Om du vill anpassa postkopieringssemantiken deklarerar du uttryckligen en kopieringskonstruktor med önskat beteende. I följande exempel uppdateras föregående exempel med en explicit kopieringskonstruktor. Det nya kopieringsbeteendet kopierar listobjekt i stället för en listreferens när en post kopieras:

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

Du kan inte anpassa kopieringssemantiken för strukturtyper.

Viktigt!

I föregående exempel är alla egenskaper oberoende. Ingen av egenskaperna beräknas från andra egenskapsvärden. Ett with uttryck kopierar först den befintliga postinstansen with och ändrar sedan alla egenskaper eller fält som anges i uttrycket. Beräknade egenskaper i record typer ska beräknas vid åtkomst, inte initieras när instansen skapas. Annars kan en egenskap returnera det beräknade värdet baserat på den ursprungliga instansen, inte den ändrade kopian. Mer information finns i språkreferensartikeln om record typer.

Språkspecifikation för C#

Mer information finns i följande avsnitt i -posternas funktionsförslagsanteckning:

Se även