다음을 통해 공유


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# 언어 사양

자세한 내용은 레코드 기능 제안 참고 사항의 다음 섹션을 참조하세요.

참고하십시오