Freigeben über


Der with Ausdruck - Nicht destruktive Mutation erstellt ein neues Objekt mit geänderten Eigenschaften.

Ein with Ausdruck erstellt eine Kopie des Operanden mit den angegebenen Eigenschaften und Feldern, die geändert wurden. Verwenden Sie die Objektinitialisierungssyntax , um anzugeben, welche Elemente geändert werden sollen, und deren neue Werte:

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

Der linke Operand eines with Ausdrucks kann ein Datensatztyp sein. Es kann auch ein Strukturtyp oder ein anonymer Typ sein.

Die C#-Sprachreferenz dokumentiert die zuletzt veröffentlichte Version der C#-Sprache. Außerdem enthält sie eine erste Dokumentation zu Funktionen in der öffentlichen Vorschau für die kommende Sprachversion.

In der Dokumentation werden alle Features identifiziert, die in den letzten drei Versionen der Sprache oder in der aktuellen öffentlichen Vorschau eingeführt wurden.

Tipp

Informationen dazu, wann ein Feature erstmals in C# eingeführt wurde, finden Sie im Artikel zum Versionsverlauf der C#-Sprache.

Das Ergebnis eines with-Ausdrucks weist denselben Laufzeittyp auf wie der Operand des Ausdrucks. Sehen Sie sich dazu das folgende Beispiel an:

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

Wenn ein Element ein Verweistyp ist, kopiert das Kopieren dieses Elements nur den Verweis auf diese Memberinstanz. Sowohl die Kopie als auch der ursprüngliche Operand greifen auf dieselbe Verweistypinstanz zu. Im folgenden Beispiel wird dieses Verhalten veranschaulicht:

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

Benutzerdefinierte Kopiersemantik

Jeder Datensatzklassentyp verfügt über einen Kopierkonstruktor. Bei einem Kopierkonstruktor handelt es sich um einen Konstruktor mit einzelnem Parameter des enthaltenden Datensatztyps. Der Zustand des dazugehörigen Arguments wird in eine neue Datensatzinstanz kopiert. Wenn Sie einen with Ausdruck auswerten, ruft er den Kopierkonstruktor auf, um eine neue Datensatzinstanz basierend auf einem ursprünglichen Datensatz zu erstellen. Anschließend wird die neue Instanz mit den angegebenen Änderungen aktualisiert. Standardmäßig synthetisiert der Compiler den Kopierkonstruktor. Um die Semantik der Datensatzkopie anzupassen, deklarieren Sie explizit einen Kopierkonstruktor mit dem gewünschten Verhalten. Das folgende Beispiel aktualisiert das vorherige Beispiel mit einem expliziten Kopierkonstruktor. Das neue Kopierverhalten kopiert Listenelemente anstelle eines Listenverweises, wenn ein Datensatz kopiert wird:

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

Die Kopiersemantik für Strukturtypen kann nicht angepasst werden.

Von Bedeutung

In den vorherigen Beispielen sind alle Eigenschaften unabhängig. Keine der Eigenschaften wird aus anderen Eigenschaftswerten berechnet. Ein with Ausdruck kopiert zuerst die vorhandene Datensatzinstanz und ändert dann alle im with Ausdruck angegebenen Eigenschaften oder Felder. Berechnete Eigenschaften in record Typen sollten beim Zugriff berechnet und nicht initialisiert werden, wenn die Instanz erstellt wird. Andernfalls gibt eine Eigenschaft den berechneten Wert basierend auf der ursprünglichen Instanz und nicht auf der geänderten Kopie zurück. Weitere Informationen finden Sie im Sprachreferenzartikel zu record Typen.

C#-Sprachspezifikation

Weitere Informationen finden Sie in den folgenden Abschnitten des Artikels Datensätze:

Weitere Informationen