次の方法で共有


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式の左側のオペランドには、レコード型を指定できます。 構造体型または匿名型を指定することもできます。

C# 言語リファレンスには、C# 言語の最新リリース バージョンが記載されています。 また、今後の言語リリースのパブリック プレビューの機能に関する初期ドキュメントも含まれています。

このドキュメントでは、言語の最後の 3 つのバージョンまたは現在のパブリック プレビューで最初に導入された機能を特定します。

ヒント

C# で機能が初めて導入された時期を確認するには、 C# 言語バージョン履歴に関する記事を参照してください。

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

カスタム コピー セマンティクス

すべてのレコード クラス型には 、コピー コンストラクターがありますコピー コンストラクター は、含まれるレコード型の 1 つのパラメーターを持つコンストラクターです。 引数の状態を新しいレコード インスタンスにコピーします。 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
    }
}

構造体型のコピー セマンティクスをカスタマイズすることはできません。

Important

前の例では、すべてのプロパティが独立しています。 他のプロパティ値から計算されるプロパティはありません。 with式は、まず既存のレコード インスタンスをコピーしてから、with式で指定されたプロパティまたはフィールドを変更します。 record型の計算されたプロパティは、インスタンスの作成時に初期化されるのではなく、アクセス時に計算する必要があります。 それ以外の場合、プロパティは、変更されたコピーではなく、元のインスタンスに基づいて計算値を返す可能性があります。 詳細については、 recordに関する言語リファレンス記事を参照してください。

C# 言語仕様

詳細については、レコード機能提案ノートの次のセクションを参照してください。

関連項目