다음을 통해 공유


속성의 field 키워드

요약

모든 속성을 확장하여 새 컨텍스트 키워드 field사용하여 자동으로 생성된 백업 필드를 참조할 수 있도록 합니다. 이제 속성은 본문을 있는 접근자 함께 본문을 않고도 접근자 포함할 수 있습니다.

동기

자동 속성은 지원 필드를 직접 설정하거나 가져오는 것만 허용하므로 접근자에 액세스 한정자를 배치해야만 제어할 수 있습니다. 때때로 하나 또는 양쪽 모두 접근자에서 일어나는 일을 추가로 제어해야 할 필요가 있습니다. 하지만, 이는 사용자가 지원 필드를 선언해야 하는 오버헤드에 직면하게 만듭니다. 그런 다음 지원 필드 이름을 속성과 동기화된 상태로 유지해야 하며, 지원 필드는 전체 클래스에 범위가 지정되므로 클래스 내에서 접근자가 실수로 우회될 수 있습니다.

몇 가지 일반적인 시나리오가 있습니다. getter 내에는 속성이 한 번도 지정되지 않은 경우 사용되는 지연 초기화 또는 기본값이 있습니다. setter 내에서 값의 유효성을 보장하기 위해 제약 조건을 적용하거나, 업데이트를 검색하고 전파하기 위해 INotifyPropertyChanged.PropertyChanged 이벤트를 발생시키는 등과 같은 작업을 수행합니다.

이러한 경우 이제는 항상 인스턴스 필드를 만들고 전체 속성을 직접 작성해야 합니다. 이렇게 하면 상당한 양의 코드가 추가될 뿐만 아니라 접근자의 본문에서만 사용할 수 있도록 하는 것이 바람직할 때 지원 필드가 형식의 나머지 범위로 누출됩니다.

용어집

  • 자동 속성: "자동으로 구현된 속성"(§15.7.4)의 약식입니다. 자동 속성의 접근자에는 본문이 없습니다. 구현 및 백업 스토리지는 모두 컴파일러에서 제공합니다. 자동 속성에는 { get; }, { get; set; }또는 { get; init; }있습니다.

  • 자동 접근자: "자동으로 구현된 접근자"의 줄임말입니다. 본문이 없는 접근자입니다. 구현 및 백업 스토리지는 모두 컴파일러에서 제공합니다. get;, set;init; 자동 접근자입니다.

  • 완전한 접근자: 본문을 가진 접근자입니다. 구현은 컴파일러에서 제공되지 않지만 백업 스토리지는 여전히 있을 수 있습니다(예: set => field = value;).

  • 필드 지원 속성 : 이는 접근자 본문 내에서 field 키워드를 사용하는 속성이거나 자동 생성 속성입니다.

  • 지원 필드: 이는 속성 접근자에서 field 키워드로 나타내는 변수로, 자동으로 구현된 접근자(get;, set;또는 init;)에서 암시적으로 읽히거나 쓰입니다.

상세 디자인

init 접근자가 있는 속성의 경우 set 아래에 적용되는 모든 항목이 init 접근자에 대신 적용됩니다.

두 가지 구문 변경이 있습니다.

  1. 새로운 컨텍스트 키워드 field는 속성 선언을 위한 지원 필드에 접근하기 위해 속성 접근자 내부에서 사용할 수 있습니다 (LDM 의사 결정).

  2. 이제 속성에 자동 접근자와 전체 접근자를 혼합하여 사용할 수 있습니다(LDM 의사 결정). "자동 속성"은 접근자의 본문이 없는 속성을 계속 의미합니다. 아래 예제 중 어느 것도 자동 속성으로 간주되지 않습니다.

예제:

{ get; set => Set(ref field, value); }
{ get => field ?? parent.AmbientValue; set; }

두 접근자는 각각 또는 둘 다 field을 사용할 수 있는 전체 접근자일 수 있습니다.

{ get => field; set => field = value; }
{ get => field; set => throw new InvalidOperationException(); }
{ get => overriddenValue; set => field = value; }
{
    get;
    set
    {
        if (field == value) return;
        field = value;
        OnXyzChanged();
    }
}

식 본문 속성 및 get 접근자만 있는 속성도 field을 사용할 수 있습니다.

public string LazilyComputed => field ??= Compute();
public string LazilyComputed { get => field ??= Compute(); }

설정 전용 속성도 field를 사용할 수 있습니다.

{
    set
    {
        if (field == value) return;
        field = value;
        OnXyzChanged(new XyzEventArgs(value));
    }
}

주요 변경 내용

속성 접근자 본문에서 field 상황별 키워드의 존재는, 더 큰 호환성이 손상되는 변경 기능의 일부로 제안된 잠재적인 호환성 손상 변경입니다.

field은 식별자가 아닌 키워드이기 때문에, @field이라는 일반 키워드 이스케이프 경로를 사용하여 식별자에 의해 가려질 수 있습니다. 속성 접근자 본문 내에 선언된 field로 명명된 모든 식별자는 초기 @을 추가하여 C# 버전 13 이전에서 업그레이드할 때 발생할 수 있는 중단으로부터 보호할 수 있습니다.

필드를 겨냥한 특성

자동 속성과 마찬가지로 해당 접근자 중 하나에서 지원 필드를 사용하는 모든 속성은 필드 대상 특성을 사용할 수 있습니다.

[field: Xyz]
public string Name => field ??= Compute();

[field: Xyz]
public string Name { get => field; set => field = value; }

접근자가 백업 필드를 사용하지 않는 한 필드 대상 속성은 유효하지 않은 상태로 유지됩니다.

// ❌ Error, will not compile
[field: Xyz]
public string Name => Compute();

속성 이니셜라이저

초기화가 있는 속성은 field을 사용할 수 있습니다. 지원 필드는 setter가 호출되지 않고 직접 초기화됩니다 (LDM의 의사 결정).

이니셜라이저에 대해 setter를 호출하는 것은 옵션이 아닙니다. 이니셜라이저는 기본 생성자를 호출하기 전에 처리되며 기본 생성자를 호출하기 전에 인스턴스 메서드를 호출하는 것은 불법입니다. 구조체의 기본 초기화/명확한 할당에도 중요합니다.

이렇게 하면 초기화를 유연하게 제어할 수 있습니다. setter를 호출하지 않고 초기화하려면 속성 이니셜라이저를 사용합니다. setter를 호출하여 초기화하려는 경우 생성자에서 속성에 초기 값을 할당하는 데 사용합니다.

이것이 유용한 예는 다음과 같습니다. field 키워드는 INotifyPropertyChanged 패턴에 대해 제공하는 우아한 솔루션 때문에 보기 모델에서 많은 사용을 찾을 수 있다고 생각합니다. 뷰 모델 속성 설정자는 UI에 데이터 바인딩될 가능성이 높으며, 변경 추적을 유발하거나 다른 동작을 트리거할 수 있습니다. 다음 코드는 IsActive의 기본값을 초기화해야 하며, HasPendingChangestrue로 설정하지 않아야 합니다.

class SomeViewModel
{
    public bool HasPendingChanges { get; private set; }

    public bool IsActive { get; set => Set(ref field, value); } = true;

    private bool Set<T>(ref T location, T value)
    {
        if (RuntimeHelpers.Equals(location, value))
            return false;

        location = value;
        HasPendingChanges = true;
        return true;
    }
}

속성 이니셜라이저와 생성자에서 할당 간의 이러한 동작 차이는 이전 버전의 언어에서 가상 자동 속성에서도 확인할 수 있습니다.

using System;

// Nothing is printed; the property initializer is not
// equivalent to `this.IsActive = true`.
_ = new Derived();

class Base
{
    public virtual bool IsActive { get; set; } = true;
}

class Derived : Base
{
    public override bool IsActive
    {
        get => base.IsActive;
        set
        {
            base.IsActive = value;
            Console.WriteLine("This will not be reached");
        }
    }
}

생성자 할당

자동 속성과 마찬가지로 생성자의 할당은 존재하는 경우 (잠재적으로 가상) setter를 호출하고, setter가 없으면 백업 필드에 직접 할당하는 것으로 대체됩니다.

class C
{
    public C()
    {
        P1 = 1; // Assigns P1's backing field directly
        P2 = 2; // Assigns P2's backing field directly
        P3 = 3; // Calls P3's setter
        P4 = 4; // Calls P4's setter
    }

    public int P1 => field;
    public int P2 { get => field; }
    public int P4 { get => field; set => field = value; }
    public int P3 { get => field; set; }
}

구조체의 명확한 할당

생성자에서 참조할 수 없더라도 field 키워드로 표시되는 지원 필드는 다른 구조체 필드(LDM 의사 결정 1, LDM 의사 결정 2)와 동일한 조건에서 기본 초기화 및 기본적으로 비활성화된 경고가 적용됩니다.

예를 들어 (이러한 진단은 기본적으로 무음임):

public struct S
{
    public S()
    {
        // CS9020 The 'this' object is read before all of its fields have been assigned, causing preceding implicit
        // assignments of 'default' to non-explicitly assigned fields.
        _ = P1;
    }

    public int P1 { get => field; }
}
public struct S
{
    public S()
    {
        // CS9020 The 'this' object is read before all of its fields have been assigned, causing preceding implicit
        // assignments of 'default' to non-explicitly assigned fields.
        P2 = 5;
    }

    public int P2 { get => field; set => field = value; }
}

참조 반환 속성

자동 속성과 마찬가지로 field 키워드는 ref-returning 속성에서 사용할 수 없습니다. 참조 반환 속성에는 set 접근자를 사용할 수 없으며 set 접근자가 없으면 get 접근자와 속성 이니셜라이저만 지원 필드에 액세스할 수 있습니다. 이에 대한 사용 사례가 없으므로 이제 참조 반환 속성이 자동 속성으로 작성되기 시작할 때가 아닙니다.

Null 허용 여부

Nullable 참조 형식 기능의 원칙은 C#의 기존 관용적인 코딩 패턴을 이해하고 이러한 패턴에 대해 가능한 한 간소하게 요구하는 것이었습니다. field 키워드 제안을 사용하면 관용적인 패턴이 지연 초기화된 프로퍼티와 같이 많이 요청된 시나리오를 해결할 수 있습니다. Nullable 참조 형식은 이러한 새로운 코딩 패턴과 잘 어울러야 합니다.

목표:

  • field 키워드 기능의 다양한 사용 패턴에 대해 적절한 수준의 null 안전성을 보장해야 합니다.

  • field 키워드를 사용하는 패턴은 항상 언어의 일부인 것처럼 느껴집니다. field 키워드 기능에 완벽히 맞는 코드에서 Nullable 참조 형식을 활성화하는데, 사용자가 불필요한 복잡함 없이 쉽게 할 수 있도록 하세요.

주요 시나리오 중 하나는 속성의 지연 초기화입니다.

public class C
{
    public C() { } // It would be undesirable to warn about 'Prop' being uninitialized here

    string Prop => field ??= GetPropValue();
}

다음 null 허용 여부 규칙은 field 키워드를 사용하는 속성뿐만 아니라 기존 자동 속성에도 적용됩니다.

지원 필드 Null 허용 여부

새 용어의 정의는 용어집 참조하세요.

의 백업 필드은 속성과 동일한 형식입니다. 그러나 nullable 주석이 속성과 다를 수 있습니다. 이 nullable 주석을 확인하기 위해 null 복원력개념을 소개합니다. Null-resilience 직관적으로 이는 속성의 get 접근자가 필드에 해당 형식의 default 값을 포함하고 있는 경우에도 null 안전성을 유지한다는 것을 의미합니다.

필드 지원 속성 접근자의 특별한 nullable 분석을 수행하여 null 복원력 결정됩니다.

  • 이 분석을 위해 field 일시적으로 주석이 null 허용 여부(예: string?)가 있다고 가정합니다. 이로 인해 field은 형식에 따라 이 null일 수도 있는 상태나 이 기본값일 수도 있는 초기 상태를 get 접근자에서 가질 수 있습니다.
  • 그런 다음 getter의 nullable 분석에서 nullable 경고를 생성하지 않으면 속성이 null 안전입니다. 그렇지 않으면 null 복원력이 없습니다.
  • 속성에 get 접근자가 없으면 null 복원력이 있는 것입니다.
  • get 접근자가 자동으로 구현된 경우 속성은 null에 견딜 수 없습니다.

백킹 필드의 null 허용 여부는 다음과 같이 결정됩니다.

  • 필드에 [field: MaybeNull], AllowNull, NotNull또는 DisallowNull같은 null 허용 여부 특성이 있는 경우 필드의 nullable 주석은 속성의 null 허용 주석과 동일합니다.
    • 이는 사용자가 필드에 nullability 특성을 적용하기 시작하면 더 이상 아무것도 유추하지 않고, 단지 사용자가말한 대로의 null 허용 여부만을 원하기 때문입니다.
  • 포함하는 속성에 기억하지 못하는 또는 주석이 추가된 null 허용 여부가 있는 경우, 지원 필드는 속성과 동일한 null 허용 여부를 가집니다.
  • 포함하는 속성에 주석이 추가되지 않은 null 허용 여부(예: 또는 )가 있거나 특성이 있고 속성이 null 복원력경우 지원 필드에 주석이 추가된 null 허용 여부가 .
  • 포함된 속성에 주석이 추가되지 않은 null 허용 여부(예: 또는 )가 있거나 특성이 있고 속성이 null 복원력경우 지원 필드에 주석이 추가되지 않은 null 허용 여부가 .

생성자 분석

현재 자동 속성은 nullable 생성자 분석일반 필드와 매우 유사하게 처리됩니다. 필드 지원 속성에 대한 처리는 모든 필드 지원 속성을 해당 지원 필드의 프록시로 간주함으로써 확장됩니다.

이 작업을 수행하기 위해 이전의 제안된 접근 방식에서으로 사양 언어를 다음과 같이 업데이트합니다.

생성자의 각 명시적 또는 암시적 'return'에서, 흐름 상태가 주석 및 Null 허용 여부 속성과 호환되지 않는 각 멤버 변수에 대해 경고를 제공합니다. 멤버가 필드 기반 속성인 경우, 해당 필드의 nullable 주석이 이 검사에 사용됩니다. 그렇지 않으면 멤버 자체의 nullable 주석이 사용됩니다. 이에 대한 적절한 프록시는: 반환 지점에서 멤버를 자체에 할당할 때 널 허용 여부 경고가 발생한다면, 반환 지점에서도 널 허용 여부 경고가 발생한다는 것입니다.

이는 본질적으로 제한된 프로시저 간 분석입니다. 생성자를 분석하려면 동일한 유형 내의 모든 해당 get 접근자에서 field 컨텍스트 키워드를 사용하고 으로 주석이 추가되지 않은 null 허용성을 가진 경우에 대해 바인딩 및 "null 복원력" 분석을 수행해야 합니다. getter의 본문은 보통 크게 복잡하지 않기 때문에 엄청난 비용이 들지 않을 것으로, 형식에 있는 생성자 수에 관계없이 "null 복원력" 분석은 한 번만 수행해도 될 것이라고 추측합니다.

Setter 분석

간단히 하기 위해 "setter" 및 "set accessor"라는 용어를 사용하여 set 또는 init 접근자를 참조합니다.

필드 지원 속성의 setter가 실제로 지원 필드를 초기화할 확인해야 합니다.

class C
{
    string Prop
    {
        get => field;

        // getter is not null-resilient, so `field` is not-annotated.
        // We should warn here that `field` may be null when exiting.
        set { }
    }

    public C()
    {
        Prop = "a"; // ok
    }

    public static void Main()
    {
        new C().Prop.ToString(); // NRE at runtime
    }
}

필드로 구현된 속성의 setter에서,의 지원 필드의 초기 흐름 상태는 다음과 같이 결정됩니다.

  • 속성에 이니셜라이저가 있는 경우 초기 흐름 상태는 이니셜라이저를 방문한 후 속성의 흐름 상태와 동일합니다.
  • 그렇지 않으면 초기 흐름 상태는 field = default;지정된 흐름 상태와 동일합니다.

setter의 각 명시적 또는 암시적 'return'에서 지원 필드 흐름 상태가 주석 및 null 허용 여부 특성과 호환되지 않는 경우 경고가 보고됩니다.

발언

이 공식은 의도적으로 생성자의 일반 필드와 매우 유사합니다. 기본적으로 속성 접근자만 실제로 지원 필드를 참조할 수 있으므로 setter는 지원 필드에 대한 "미니 생성자"로 처리됩니다.

일반 필드와 마찬가지로, 속성이 설정되었기 때문에 생성자에서 초기화된 것으로 보이지만, 반드시 그렇지는 않습니다. 추적되지 않은 메커니즘이 속성을 설정하는 데 사용되었을 수 있음을 이해하므로 Prop != null true인 분기 내에서 반환하는 것만으로도 생성자 분석에 충분합니다.

대안이 고려되었다; Null 가능성 대안 섹션을 참조하세요.

nameof

field가 키워드로 사용되는 경우에는 nameof(field)nameof(nint)처럼 컴파일하지 않습니다 (LDM 의사 결정). nameof(value)와 같은 것은 아닙니다. 이는 .NET Core 라이브러리 중 일부에서 속성 setter가 ArgumentException을 throw할 때 사용되는 것입니다. 반면, nameof(field) 예상되는 사용 사례가 없습니다.

재정의

재정의된 속성에 field을 사용할 수 있습니다. 이러한 field 사용은 재정의된 속성의 지원 필드를 참조하며, 기본 속성이 지원 필드를 가지고 있는 경우 그것과는 별개입니다. 캡슐화가 중단되므로 기본 속성의 지원 필드를 재정의 클래스에 노출하는 ABI는 없습니다.

자동 속성과 마찬가지로 field 키워드를 사용하고 기본 속성을 재정의하는 속성은 모든 접근자(LDM 의사 결정)를 재정의해야 합니다.

캡처

field 로컬 함수 및 람다에서 캡처할 수 있어야 하며, 다른 참조가 없더라도 로컬 함수 및 람다 내부에서 field 대한 참조가 허용됩니다(LDM 의사 결정 1, LDM 의사 결정 2):

public class C
{
    public static int P
    {
        get
        {
            Func<int> f = static () => field;
            return f();
        }
    }
}

필드 사용 경고 메시지

접근자에 field 키워드를 사용하는 경우, 컴파일러의 기존 미할당 필드나 읽히지 않은 필드에 대한 분석 범위에 해당 필드가 포함됩니다.

  • CS0414: 'Xyz' 속성에 대한 지원 필드가 할당되었지만 해당 값은 사용되지 않습니다.
  • CS0649: 'Xyz' 속성의 지원 필드는 할당되지 않으며 항상 기본값을 갖습니다.

사양 변경

통사론

언어 버전 13 이상으로 컴파일할 때, 다음 위치(LDM 의사 결정)에서, 기본 식(LDM 의사 결정)로 사용될 경우 field는 키워드로 간주됩니다.

  • 속성 get, setinit 접근자에서 메서드 본문은 가능하지만 인덱서에서는 불가능합니다.
  • 해당 접근자에 적용되는 특성
  • 중첩된 람다 식 및 로컬 함수, 그리고 해당 접근자에 있는 LINQ 식에서

언어 버전 12 이하로 컴파일하는 경우를 포함하여 다른 모든 경우에서 field 식별자로 간주됩니다.

primary_no_array_creation_expression
    : literal
+   | 'field'
    | interpolated_string_expression
    | ...
    ;

속성

§15.7.1속성 - 일반

자동으로 구현된 속성을 내보낸 지원 필드가 있는 속성에 대해서만 property_initializer 제공될 수 있습니다. property_initializer지정된 값을 사용하여 이러한 속성의 기본 필드를 초기화합니다.

§15.7.4자동으로 구현된 속성

자동으로 구현된 속성(또는 짧게 자동 속성)은 세미콜론 전용 접근자 본문을 비 추상적, 비-extern, 비-ref-valued 속성입니다. 자동 속성에는 get 접근자가 있어야 하며 필요에 따라 set 접근자가 있을 수 있습니다.중 하나 또는 둘 다:

  1. 세미콜론 전용 본문을 가진 접근자
  2. 속성의 접근자 또는식 본문 내에서 상황별 키워드의 사용

속성이 자동으로 구현된 속성으로 지정되면 속성 숨겨진 이름 없는 지원 필드를 자동으로 사용할 수 있으며, 접근자는 해당 지원 필드읽고 쓰도록 구현됩니다. 자동 속성의 경우, 세미콜론으로만 이루어진 get 접근자는 읽기용으로 구현되며, 세미콜론으로만 이루어진set 접근자는 지원 필드에 기록할 수 있도록 구현됩니다.

숨겨진 지원 필드에 액세스할 수 없는 포함된 형식 내에서도 자동으로 구현된 속성 접근자를 통해서만 읽고 쓸 수 있습니다.모든 접근자 및 속성 식 본문 내에서 field 키워드사용하여 지원 필드를 직접 참조할 수 있습니다. 필드가 명명되지 않으므로nameof 식에서 사용할 수 없습니다.

auto 속성에 set 접근자 없고 세미콜론만 있는 get 접근자경우, 지원 필드는 (§15.5.3)로 간주됩니다. readonly 필드와 마찬가지로, 집합 접근자나 init 접근자 없이 읽기 전용 자동 속성 도 해당 클래스의 생성자 본문에 할당할 수 있습니다. 이러한 할당은 속성의 읽기 전용 지원 필드에 직접 할당됩니다.

자동 속성은 get 접근자 없이 단일 세미콜론 전용 set 접근자만 가질 수 없습니다.

자동 속성에는 선택적으로 property_initializer가 있을 수 있으며, 이는 variable_initializer(§17.7)로 지원 필드에 직접 적용됩니다.

다음 예제는 다음과 같습니다.

// No 'field' symbol in scope.
public class Point
{
    public int X { get; set; }
    public int Y { get; set; }
}

는 다음 선언과 동일합니다.

// No 'field' symbol in scope.
public class Point
{
    public int X { get { return field; } set { field = value; } }
    public int Y { get { return field; } set { field = value; } }
}

은 다음과 같습니다.

// No 'field' symbol in scope.
public class Point
{
    private int __x;
    private int __y;
    public int X { get { return __x; } set { __x = value; } }
    public int Y { get { return __y; } set { __y = value; } }
}

다음 예제는 다음과 같습니다.

// No 'field' symbol in scope.
public class LazyInit
{
    public string Value => field ??= ComputeValue();
    private static string ComputeValue() { /*...*/ }
}

는 다음 선언과 동일합니다.

// No 'field' symbol in scope.
public class Point
{
    private string __value;
    public string Value { get { return __value ??= ComputeValue(); } }
    private static string ComputeValue() { /*...*/ }
}

대안

Null 허용 여부 대안

Null 허용 여부 섹션에 설명된 null 복원력 방법 외에도 작업 그룹은 LDM의 고려 사항에 대해 다음과 같은 대안을 제안했습니다.

아무 것도 수행하지 않음

여기서는 특별한 행동을 전혀 도입할 수 없습니다. 적용:

  • 필드 기반 속성을 자동 속성이 처리되는 것과 동일한 방식으로 처리합니다. 필수로 표시된 경우를 제외하고, 생성자에서 초기화해야 합니다.
  • 속성 접근자를 분석할 때 필드 변수에 대한 특별한 처리가 없습니다. 속성과 동일한 형식 및 null 허용성을 가진 변수일 뿐입니다.

"참고로, 이는 '지연 속성' 시나리오에서 거슬리는 경고를 야기할 수 있습니다. 이 경우, 사용자는 생성자 경고를 무시하기 위해 null! 또는 이와 유사한 것을 할당해야 할 수 있습니다."
고려할 수 있는 "하위 대안"으로는 nullable 생성자 분석에서 field 키워드를 사용하여 속성을 또한 완전히 무시하는 방법이 있습니다. 이 경우 사용자가 어떤 것을 초기화해야 하는지에 대한 경고는 없지만 사용 중인 초기화 패턴에 관계없이 사용자에게는 성가신 경고가 없습니다.

.NET 9의 Preview LangVersion 아래에 field 키워드 기능만 제공할 계획이기 때문에 .NET 10의 기능에 대한 nullable 동작을 변경하는 기능이 있을 것으로 예상됩니다. 따라서 단기적으로 이와 같은 "저렴한" 솔루션을 채택하고 장기적으로 더 복잡한 솔루션 중 하나로 성장하는 것을 고려할 수 있습니다.

field대상 null 허용 여부 특성

다음과 같은 기본값을 도입하여 프로시전 간 분석을 전혀 포함하지 않고 적절한 수준의 null 안전성을 달성할 수 있습니다.

  1. field 변수에는 항상 속성과 동일한 nullable 주석이 있습니다.
  2. Null 허용 속성 [field: MaybeNull, AllowNull] 등을 사용하여 백킹 필드의 null 허용 가능성을 특정하게 설정할 수 있습니다.
  3. 필드 기반 속성은 생성자에서 필드의 nullable 주석 및 특성에 따라 초기화 여부를 확인합니다.
  4. 필드 기반 속성의 setter는 생성자처럼 field의 초기화를 확인합니다.

즉, "little-l lazy 시나리오"는 대신 다음과 같이 보일 것입니다.

class C
{
    public C() { } // no need to warn about initializing C.Prop, as the backing field is marked nullable using attributes.

    [field: AllowNull, MaybeNull]
    public string Prop => field ??= GetPropValue();
}

여기서 Null 허용 여부 특성을 사용하지 않는 한 가지 이유는 서명의 입력 및 출력을 설명하는 데 중심을 두기 때문입니다. 수명이 긴 변수의 null 허용 가능성을 설명하는 데 사용하는 것이 번거롭습니다.

  • 실제로 필드가 null 허용 변수로 "합리적으로" 동작하도록 하기 위해서는 [field: MaybeNull, AllowNull]이 필요합니다. 이는 필드가 null일 가능성이 있는 초기 흐름 상태를 가지게 하고, 가능한 null 값을 기록할 수 있도록 합니다. 이렇게 하면 사용자에게 비교적 일반적인 "little-l lazy" 시나리오를 수행하도록 요청하는 것이 번거로울 수 있습니다.
  • 만약 우리가 이 방식을 따르게 된다면, [field: AllowNull]이 사용될 때 경고를 추가하는 것과 함께 MaybeNull을 추가할 것을 권장합니다. AllowNull을 사용하면 nullable 변수에서 사용자가 필요로 하는 작업이 수행되지 않기 때문입니다. 이는 필드에 어떤 작업도 이루어지지 않았을 때, 필드가 처음에는 null이 아닌 것으로 가정하기 때문입니다.
  • 우리는 또한 field 키워드나 일반적인 필드의 [field: MaybeNull] 동작을 조정하여 AllowNull가 암시적으로 존재하는 것처럼 null을 변수에 작성할 수 있도록 고려할 수 있습니다.

답변된 LDM 질문

키워드의 구문 위치

접근자들에서 fieldvalue이 합성된 지원 필드 또는 암시적 setter 매개 변수에 바인딩될 수 있을 때, 어떤 구문 위치에서는 식별자를 키워드로 간주해야 합니까?

  1. 주요 표현
  2. 결코

처음 두 가지 경우는 호환성이 손상되는 변경입니다.

식별자가 항상 이(가) 키워드로 간주될 경우, 예를 들어 다음과 같이 호환성에 문제가 생길 수 있는 변경 사항입니다.

class MyClass
{
    private int field;
    public int P => this.field; // error: expected identifier

    private int value;
    public int Q
    {
        set { this.value = value; } // error: expected identifier
    }
}

기본 식으로만 사용할 때 식별자가 키워드인 경우, 변경으로 인한 호환성 손상의 영향이 더 적습니다. 가장 일반적인 중단은 field라는 기존 멤버를 제한 없이 사용함으로 인해 발생할 수 있습니다.

class MyClass
{
    private int field;
    public int P => field; // binds to synthesized backing field rather than 'this.field'
}

중첩된 함수에서 field 또는 value 다시 선언될 때도 중단이 발생합니다. 이는기본 식에 대한 유일한 중단일 수 있습니다.

class MyClass
{
    private IEnumerable<string> _fields;
    public bool HasNotNullField
    {
        get => _fields.Any(field => field is { }); // 'field' binds to synthesized backing field
    }
    public IEnumerable<string> Fields
    {
        get { return _fields; }
        set { _fields = value.Where(value => Filter(value)); } // 'value' binds to setter parameter
    }
}

식별자가 고려된 키워드를 않을 경우 식별자는 식별자가 다른 멤버에 바인딩되지 않는 경우에만 합성된 지원 필드 또는 암시적 매개 변수에 바인딩됩니다. 이 경우 중대한 변경 사항은 없습니다.

대답

field 기본 식으로만 사용되는 경우 적절한 접근자의 키워드로. value 키워드로 간주되지 않습니다.

{ set; } 유사한 시나리오

{ set; }는 현재 허용되지 않으며, 이는 타당합니다. 이로 인해 생성되는 필드는 읽을 수 없습니다. 이제 setter가 읽지 않는 지원 필드를 도입할 수 있는 새롭고 다양한 방법이 있으며, 이는 { set; }{ set => field = value; }로 확장되는 경우와 같은 상황에서도 발생할 수 있습니다.

이러한 시나리오 중 컴파일을 허용해야 하는 시나리오는 무엇인가요? 수동으로 선언된 필드와 마찬가지로 "필드를 읽지 않습니다." 경고가 적용된다고 가정합니다.

  1. { set; } - 오늘 허용되지 않음, 계속 허용하지 않음
  2. { set => field = value; }
  3. { get => unrelated; set => field = value; }
  4. { get => unrelated; set; }
  5. {
        set
        {
            if (field == value) return;
            field = value;
            SendEvent(nameof(Prop), value);
        }
    }
    
  6. {
        get => unrelated;
        set
        {
            if (field == value) return;
            field = value;
            SendEvent(nameof(Prop), value);
        }
    }
    

대답

자동 속성의 바디리스 set;에서 이미 허용되지 않는 것만 금지합니다.

이벤트 접근자의 field

field가 이벤트 접근자의 키워드여야 하며, 컴파일러가 지원 필드를 생성해야 하나요?

class MyClass
{
    public event EventHandler E
    {
        add { field += value; }
        remove { field -= value; }
    }
}

권장 사항: 이벤트 접근자 내에서 키워드를 않고 백업 필드가 생성되지 .

대답

권장 사항이 수행되었습니다. field, 는 이벤트 접근자 내에서 키워드가 아니며, 따라서 백업 필드가 생성되지 않습니다.

field Null 허용 여부

제안된 field 허용되어야 하나요? Null 허용 여부 섹션 및 열려 있는 질문을 참조하세요.

대답

일반 제안이 채택됩니다. 특정 동작은 여전히 더 많은 검토가 필요합니다.

속성 이니셜라이저의 field

field이 속성 이니셜라이저에서 키워드로 사용되어야 하고 기본 필드에 바인딩되어야 하나요?

class A
{
    const int field = -1;

    object P1 { get; } = field; // bind to const (ok) or backing field (error)?
}

이니셜라이저에서 지원 필드를 참조하는 데 유용한 시나리오가 있나요?

class B
{
    object P2 { get; } = (field = 2);        // error: initializer cannot reference instance member
    static object P3 { get; } = (field = 3); // ok, but useful?
}

위의 예제에서 지원 필드에 바인딩하면 "이니셜라이저가 비정적 필드를 참조할 수 없습니다."라는 오류가 발생합니다.

대답

이전 버전의 C#에서와 같이 이니셜라이저를 바인딩합니다. 지원 필드를 스코프에 포함하지 않고, 또한 field이라는 이름의 다른 멤버들을 참조하는 것을 막지 않을 것입니다.

부분 속성과의 상호 작용

이니셜라이저

partial 속성이 field를 사용할 때, 어떤 부분에 초기값 설정자를 허용해야 합니까?

partial class C
{
    public partial int Prop { get; set; } = 1;
    public partial int Prop { get => field; set => field = value; } = 2;
}
  • 두 부분 모두 이니셜라이저가 있을 때 오류가 발생하는 것이 분명해 보입니다.
  • 정의 또는 구현 부분에서 field초기 값을 설정하려는 사용 사례를 생각할 수 있습니다.
  • 정의 부분에서 이니셜라이저를 허용하는 경우 프로그램이 유효하기 위해 구현자가 field 사용하도록 강제하는 것처럼 보입니다. 괜찮아?
  • 구현에서 동일한 형식의 지원 필드가 필요할 때마다 생성기에서 field 사용하는 것이 일반적이라고 생각합니다. 이는 생성기가 사용자가 속성 정의 부분에서 [field: ...] 대상 특성을 사용할 수 있도록 하려는 경우가 많기 때문입니다. field 키워드를 사용하면 생성기 구현자가 그러한 특성을 특정 생성된 필드에 할당하고, 속성에 대한 경고를 억제하여 번거로움을 덜 수 있습니다. 이러한 동일한 생성기는 사용자가 필드의 초기 값을 지정할 수 있도록 허용하려고 할 수도 있습니다.

권장 사항: 구현 부분에서 field를 사용하는 경우, 부분 속성의 어느 부분에든 이니셜라이저를 허용합니다. 두 부분에 이니셜라이저가 있으면 오류를 보고하세요.

대답

권장 사항이 수락되었습니다. 속성 위치를 선언하거나 구현하면 이니셜라이저를 사용할 수 있지만 동시에 둘 다 사용할 수는 없습니다.

자동 접근자

원래 디자인된 부분 속성 구현에는 모든 접근자에 대한 본문이 있어야 합니다. 그러나 field 키워드 기능의 최근 버전에는 "자동 접근자"라는 개념이 포함되어 있습니다. 부분 속성 구현에서 이러한 접근자를 사용할 수 있어야 하나요? 단독으로 사용되는 경우 정의 선언과 구별할 수 없습니다.

partial class C
{
    public partial int Prop0 { get; set; }
    public partial int Prop0 { get => field; set => field = value; } // this is equivalent to the two "semi-auto" forms below.

    public partial int Prop1 { get; set; }
    public partial int Prop1 { get => field; set; } // is this a valid implementation part?

    public partial int Prop2 { get; set; }
    public partial int Prop2 { get; set => field = value; } // what about this? will there be disagreement about which is the "best" style?

    public partial int Prop3 { get; }
    public partial int Prop3 { get => field; } // it will only be valid to use at most 1 auto-accessor, when a second accessor is manually implemented.

권장 사항: 부분 속성 구현에서 자동 접근자를 허용하지 않습니다. 자동 접근자를 사용할 수 있는 경우의 제한 사항은 허용의 이점보다 따르기에 더 혼란스럽기 때문입니다.

대답

하나 이상의 구현 접근자를 수동으로 구현해야 하지만 다른 접근자는 자동으로 구현될 수 있습니다.

읽기 전용 필드

합성된 지원 필드는 언제 읽기 전용로 고려해야 하나요?

struct S
{
    readonly object P0 { get => field; } = "";         // ok
    object P1          { get => field ??= ""; }        // ok
    readonly object P2 { get => field ??= ""; }        // error: 'field' is readonly
    readonly object P3 { get; set { _ = field; } }     // ok
    readonly object P4 { get; set { field = value; } } // error: 'field' is readonly
}

백업 필드가 읽기 전용것으로 간주되면 메타데이터로 내보낸 필드는 표시되고 이니셜라이저 또는 생성자가 아닌 다른 수정된 경우 오류가 보고됩니다.

권장 사항: 포함된 형식이 이고 속성 또는 포함 형식이 선언될 때 합성된 지원 필드는 읽기 전용 입니다.

대답

권장 사항이 수락됩니다.

읽기 전용 컨텍스트 및 set

field를 사용하는 속성에 대해 readonly 컨텍스트에서 set 접근자를 허용해야 하나요?

readonly struct S1
{
    readonly object _p1;
    object P1 { get => _p1; set { } }   // ok
    object P2 { get; set; }             // error: auto-prop in readonly struct must be readonly
    object P3 { get => field; set { } } // ok?
}

struct S2
{
    readonly object _p1;
    readonly object P1 { get => _p1; set { } }   // ok
    readonly object P2 { get; set; }             // error: auto-prop with set marked readonly
    readonly object P3 { get => field; set { } } // ok?
}

대답

readonly 구조체에서 set 접근자를 구현하고 이를 통과하거나 throw하는 시나리오가 있을 수 있습니다. 우리는 이것을 허용할 것입니다.

[Conditional] 코드

조건부 메서드의 생략된 호출에서만 field을 사용할 때, 합성된 필드가 생성되어야 하나요?

예를 들어 디버그가 아닌 빌드에서 다음에 대한 지원 필드를 생성해야 하나요?

class C
{
    object P
    {
        get
        {
            Debug.Assert(field is null);
            return null;
        }
    }
}

참고로, 기본 생성자 매개변수 에 대한 필드는 유사한 경우에 생성됩니다. sharplab.io에서 참조하세요.

권장 사항: 조건부 메서드대한 생략된 호출에서만 사용되는 경우 지원 필드가 생성됩니다.

대답

Conditional 코드는 Debug.Assert null 허용 여부 변경과 같은 비 조건부 코드에 영향을 줄 수 있습니다. field 비슷한 영향을 미치지 않았다면 이상할 것입니다. 또한 대부분의 코드에서 나올 가능성이 낮으므로 간단한 작업을 수행하고 권장 사항을 수락합니다.

인터페이스 속성 및 자동 접근자

자동 생성된 지원 필드를 참조하는 interface 속성에 대해 수동으로 구현된 접근자와 자동으로 구현된 접근자의 조합이 인식되는가?

인스턴스 속성의 경우 인스턴스 필드가 지원되지 않는다는 오류가 보고됩니다.

interface I
{
           object P1 { get; set; }                           // ok: not an implementation
           object P2 { get => field; set { field = value; }} // error: instance field

           object P3 { get; set { } } // error: instance field
    static object P4 { get; set { } } // ok: equivalent to { get => field; set { } }
}

권장 사항: 자동 접근자는 interface 속성에서 인식되며, 자동 접근자는 자동 생성된 백업 필드를 참조합니다. 인스턴스 속성의 경우 인스턴스 필드가 지원되지 않는다는 오류가 보고됩니다.

대답

오류의 원인이 되는 인스턴스 필드 자체를 표준화하는 것은 클래스의 부분 속성과 일치하며, 이러한 결과를 좋아합니다. 권장 사항이 수락됩니다.