共用方式為


with運算式 - 非破壞性突變會建立具有修改屬性的新物件

表達式 with 會產生其作數的複本,其中已修改指定的屬性和欄位。 您可以使用 物件初始化表示式 語法來指定要修改的成員及其新值:

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

表達式的 with 左側作數可以是 記錄類型。 表達式的 with 左側作數也可以是 結構類型匿名型別

表達式的結果 with 與表達式的作數具有相同的運行時間類型,如下列範例所示:

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

當成員是參考類型時,複製運算元時,只會複製對成員實例的參考。 複製和原始作數都能夠存取相同的參考類型實例。 下列範例示範該行為:

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

自定義複製語意

任何記錄類別類型都有 複製建構函式複製建構函式是具有包含記錄類型之單一參數的建構函式。 它會將自變數的狀態複製到新的記錄實例。 在評估 with 表達式時,會呼叫複製建構函式,以根據原始記錄具現化新的記錄實例。 之後,新的實例會根據指定的修改進行更新。 根據預設,複製建構函式是隱含的,也就是編譯程序產生的。 如果您需要自定義記錄複製語意,請明確宣告具有所需行為的複製建構函式。 下列範例會使用明確的複製建構函式來更新上述範例。 新的複製行為是複製清單專案,而不是複製記錄時的清單參考:

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

您無法自訂結構類型的複製語意。

這很重要

在上述範例中,所有屬性都是獨立的。 不會從其他屬性值計算任何屬性。 with運算式會先複製現有的記錄實例,然後修改運算式中with指定的任何內容或欄位。 類型中的 record 計算屬性應該在存取時計算,而不是在建立執行個體時初始化。 否則,屬性可能會根據原始實例傳回計算值,而不是修改後的複本。 如需詳細資訊,請參閱有關類型的語言參考文章record

C# 語言規格

如需詳細資訊,請參閱 記錄功能提案附註的下列各節:

另請參閱