Dela via


med uttryck – Icke-förstörande mutation skapar ett nytt objekt med ändrade egenskaper

Ett with uttryck genererar en kopia av operand med de angivna egenskaperna och fälten ändrade. Du använder 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 av posttyp. Från och med C# 10 kan en vänsteroperation av ett with uttryck också vara av en strukturtyp eller en anonym typ.

Resultatet av ett with uttryck har samma körningstyp som uttryckets operand, vilket visas i följande exempel:

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 det gäller en medlem av referenstyp kopieras endast referensen till en medlemsinstans när en operande kopieras. 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

Alla postklasstyper har kopieringskonstruktorn. En kopieringskonstruktor är en konstruktor med en enda parameter av den innehållande posttypen. Den kopierar argumentets tillstånd till en ny postinstans. Vid utvärdering av ett with uttryck anropas kopieringskonstruktorn för att instansiera en ny postinstans baserat på en ursprunglig post. Därefter uppdateras den nya instansen enligt de angivna ändringarna. Som standard är kopieringskonstruktorn implicit, d.v.s. kompilatorgenererad. Om du behöver 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 är att kopiera 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.

Språkspecifikation för C#

Mer information finns i följande avsnitt i anteckningen om funktionsförslag för poster:

Se även