Movatterモバイル変換


[0]ホーム

URL:


Skip to main content

This browser is no longer supported.

Upgrade to Microsoft Edge to take advantage of the latest features, security updates, and technical support.

Download Microsoft EdgeMore info about Internet Explorer and Microsoft Edge
Table of contentsExit focus mode

with expression - Nondestructive mutation creates a new object with modified properties

  • 2024-12-04
Feedback

In this article

Awith expression produces a copy of its operand with the specified properties and fields modified. You use theobject initializer syntax to specify what members to modify and their new values:

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

The left-hand operand of awith expression can be of arecord type. A left-hand operand of awith expression can also be of astructure type or ananonymous type.

The result of awith expression has the same run-time type as the expression's operand, as the following example shows:

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

In the case of a reference-type member, only the reference to a member instance is copied when an operand is copied. Both the copy and original operand have access to the same reference-type instance. The following example demonstrates that behavior:

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

Custom copy semantics

Any record class type has thecopy constructor. Acopy constructor is a constructor with a single parameter of the containing record type. It copies the state of its argument to a new record instance. At evaluation of awith expression, the copy constructor gets called to instantiate a new record instance based on an original record. After that, the new instance gets updated according to the specified modifications. By default, the copy constructor is implicit, that is, compiler-generated. If you need to customize the record copy semantics, explicitly declare a copy constructor with the desired behavior. The following example updates the preceding example with an explicit copy constructor. The new copy behavior is to copy list items instead of a list reference when a record is copied:

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

You can't customize the copy semantics for structure types.

C# language specification

For more information, see the following sections of therecords feature proposal note:

See also

Collaborate with us on GitHub
The source for this content can be found on GitHub, where you can also create and review issues and pull requests. For more information, seeour contributor guide.

Feedback

Was this page helpful?

YesNo

In this article

Was this page helpful?

YesNo