다음을 통해 공유


부분 이벤트 및 생성자

비고

이 문서는 기능 사양입니다. 사양은 기능의 디자인 문서 역할을 합니다. 여기에는 기능 디자인 및 개발 중에 필요한 정보와 함께 제안된 사양 변경 내용이 포함됩니다. 이러한 문서는 제안된 사양 변경이 완료되고 현재 ECMA 사양에 통합될 때까지 게시됩니다.

기능 사양과 완료된 구현 간에 약간의 불일치가 있을 수 있습니다. 이러한 차이는 관련 LDM(언어 디자인 모임) 노트에서 캡처됩니다.

C# 언어 표준에 기능 사양을 채택하는 프로세스에 대해 문서에서 자세히 알아볼 수 있습니다.

챔피언 발행호: https://github.com/dotnet/csharplang/issues/9058

요약

partial 이벤트 및 생성자의 한정자가 부분 메서드부분 속성/인덱서와 유사하게 선언 및 구현 부분을 구분하도록 허용합니다.

partial class C
{
    partial C(int x, string y);
    partial event Action<int, string> MyEvent;
}

partial class C
{
    partial C(int x, string y) { }
    partial event Action<int, string> MyEvent
    {
        add { }
        remove { }
    }
}

동기

C#은 부분 메서드, 속성 및 인덱서를 이미 지원합니다. 부분 이벤트 및 생성자가 없습니다.

부분 이벤트는 사용자가 정의를 작성할 수 있는 약한 이벤트 라이브러리에 유용합니다.

partial class C
{
    [WeakEvent]
    partial event Action<int, string> MyEvent;

    void M()
    {
        RaiseMyEvent(0, "a");
    }
}

그리고 라이브러리에서 제공하는 원본 생성기는 구현을 제공합니다.

partial class C
{
    private readonly WeakEvent _myEvent;

    partial event Action<int, string> MyEvent
    {
        add { _myEvent.Add(value); }
        remove { _myEvent.Remove(value); }
    }

    protected void RaiseMyEvent(int x, string y)
    {
        _myEvent.Invoke(x, y);
    }
}

부분 이벤트 및 부분 생성자는 사용자가 부분 생성자 및 이벤트 정의를 작성할 수 있는 Xamarin 과 같은 interop 코드를 생성하는 데에도 유용합니다.

partial class AVAudioCompressedBuffer : AVAudioBuffer
{
    [Export("initWithFormat:packetCapacity:")]
    public partial AVAudioCompressedBuffer(AVAudioFormat format, uint packetCapacity);

    [Export("create:")]
    public partial event EventHandler Created;
}

그리고 원본 생성기는 바인딩을 생성합니다(이 경우 Objective-C로).

partial class AVAudioCompressedBuffer : AVAudioBuffer
{
    [BindingImpl(BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)]
    public partial AVAudioCompressedBuffer(AVAudioFormat format, uint packetCapacity) : base(NSObjectFlag.Empty)
    {
        // Call Objective-C runtime:
        InitializeHandle(
            global::ObjCRuntime.NativeHandle_objc_msgSendSuper_NativeHandle_UInt32(
                this.SuperHandle,
                Selector.GetHandle("initWithFormat:packetCapacity:"),
                format.GetNonNullHandle(nameof(format)),
                packetCapacity),
            "initWithFormat:packetCapacity:");
    }

    public partial event EventHandler Created
    {
        add { /* ... */ }
        remove { /* ... */ }
    }
}

상세 디자인

일반

이벤트 선언 구문(§15.8.1)이 partial 한정자를 허용하도록 확장됩니다.

 event_declaration
-    : attributes? event_modifier* 'event' type variable_declarators ';'
+    : attributes? event_modifier* 'partial'? 'event' type variable_declarators ';'
-    | attributes? event_modifier* 'event' type member_name
+    | attributes? event_modifier* 'partial'? 'event' type member_name
         '{' event_accessor_declarations '}'
     ;

인스턴스 생성자 선언 구문(§15.11.1)이 partial 한정자를 허용하도록 확장됩니다.

 constructor_declaration
-    : attributes? constructor_modifier* constructor_declarator constructor_body
+    : attributes? constructor_modifier* 'partial'? constructor_declarator constructor_body
     ;

제안으로는 수정자를 메서드, 속성 및 형식 선언에서 마지막이 아닌 수정자들 사이 어디에서나 허용하는 것입니다.

한정자가 있는 partial 이벤트 선언은 부분 이벤트 선언 이라고 하며 지정된 이름을 가진 하나 이상의 부분 이벤트 와 연결됩니다(접근자가 없는 하나의 이벤트 선언은 여러 이벤트를 정의할 수 있습니다).

한정자가 있는 partial 생성자 선언은 부분 생성자 선언 이라고 하며 지정된 시그니처가 있는 부분 생성자와 연결됩니다.

부분 이벤트 선언은 을 지정하거나 event_accessor_declarations 한정자가 있는 경우 구현 선언이라고 합니다. 그렇지 않으면 정의 선언입니다.

부분 생성자 선언은 이라고 합니다. 그렇지 않으면 구현 선언입니다.

partial class C
{
    // defining declarations
    partial C();
    partial C(int x);
    partial event Action E, F;

    // implementing declarations
    partial C() { }
    partial C(int x) { }
    partial event Action E { add { } remove { } }
    partial event Action F { add { } remove { } }
}

부분 멤버의 정의 선언만 조회 과정에 참여하며, 사용 시점 및 메타데이터 생성 시 고려됩니다. ( 아래에 자세히 설명된 설명서 주석 제외) 구현 선언 서명은 연결된 본문의 nullable 분석에 사용됩니다.

부분 이벤트 또는 생성자:

  • 부분 형식의 멤버로만 선언할 수 있습니다.
  • 하나의 정의 및 하나의 구현 선언이 있어야 합니다.
  • abstract 수정자를 사용할 수 없습니다.
  • 인터페이스 멤버를 명시적으로 구현할 수 없습니다.

부분 이벤트는 필드와 유사하지 않습니다(§15.8.2). 즉,

  • 컴파일러에서 생성된 백업 스토리지 또는 접근자가 없습니다.
  • +=-= 작업에서만 사용 가능하며, 값으로는 사용할 수 없습니다.

부분 생성자 선언을 정의하는 경우 생성자 이니셜라이저(또는 : this(); ): base()를 사용할 수 없습니다. §15.11.2).

구문 분석 일시 정지

partial 수정자를 더 많은 컨텍스트에서 허용하는 것은 주요 변경입니다.

class C
{
    partial F() => new partial(); // previously a method, now a constructor
    @partial F() => new partial(); // workaround to keep it a method
}

class partial { }

언어 파서를 간소화하기 위해, 위에서 문법 변경을 명시적으로 지정하지 않더라도 모든 메서드와 유사한 선언(예: 로컬 함수 및 최상위 스크립트 메서드)에서 partial 한정자가 허용됩니다.

특성

결과 이벤트 또는 생성자의 특성은 해당 위치에 있는 부분 선언의 결합된 특성입니다. 결합된 특성은 지정되지 않은 순서로 연결되며 중복은 제거되지 않습니다.

부분 이벤트 선언에서는 methodattribute_target (§22.3)가 무시됩니다. 접근자 특성은 접근자 선언에서만 사용됩니다(구현 선언에서만 존재할 수 있음). 모든 이벤트 선언에서는 paramreturnattribute_target이 이미 무시됩니다.

구현 선언의 호출자 정보 특성은 섹션 호출자 정보 특성 의 partial 속성 제안에서 지정한 대로 컴파일러에서 무시됩니다(부분 이벤트 및 생성자를 포함하는 모든 부분 멤버 에 적용됨).

서명

부분 멤버의 두 선언에는 부분 속성과 유사한 일치하는 서명이 있어야 합니다.

  1. 런타임에 중요한 부분 선언 간의 형식 및 ref 종류 차이로 인해 컴파일 시간 오류가 발생합니다.
  2. 부분 선언 간의 튜플 요소 이름의 차이로 인해 컴파일 시간 오류가 발생합니다.
  3. 선언에는 동일한 한정자가 있어야 하며, 한정자는 서로 다른 순서로 나타날 수 있습니다.
    • 예외: 구현 선언에 extern 만 나타날 수 있는 한정자에는 적용되지 않습니다.
  4. 부분 선언 서명의 다른 모든 구문 차이로 인해 컴파일 시간 경고가 발생하며 다음 예외가 발생합니다.
    • 특성 목록은 위에서 설명한 대로 일치시킬 필요가 없습니다.
    • Nullable 컨텍스트 차이(예: 비활성화 vs 주석 추가)는 경고를 발생시키지 않습니다.
    • 기본 매개 변수 값은 일치시킬 필요가 없지만 구현하는 생성자 선언에 기본 매개 변수 값이 있는 경우 경고가 보고됩니다(정의 선언만 조회에 참여하므로 무시되기 때문).
  5. 생성자 선언 정의 및 구현에서 매개 변수 이름이 다를 때 경고가 발생합니다.
  6. "명시되지 않은 null 허용 여부와 관련되지 않은 null 허용 여부의 차이로 인해 경고가 발생합니다."

설명서 주석

정의 및 구현 선언 모두에 문서 주석을 포함할 수 있습니다. 문서 주석은 이벤트 접근자에서 지원되지 않습니다.

부분 멤버의 선언 중 하나에만 문서 주석이 있는 경우 이러한 문서 주석은 정상적으로 사용됩니다(Roslyn API를 통해 표시되고 설명서 XML 파일로 내보냅니다).

부분 멤버의 두 선언에 문서 주석이 있으면 정의 선언에 대한 모든 문서 주석이 삭제되고 구현 선언에 대한 문서 주석만 사용됩니다.

부분 멤버 paramref 의 선언 간에 매개 변수 이름이 다른 경우 요소는 소스 코드의 설명서 주석과 연결된 선언의 매개 변수 이름을 사용합니다. 예를 들어 paramref 구현 선언에 배치된 문서 주석은 해당 매개 변수 이름을 사용하여 구현 선언의 매개 변수 기호를 참조합니다. 메타데이터 서명이 정의 선언의 매개 변수 이름을 사용하기 때문에 혼동될 수 있습니다. 이러한 혼동을 방지하려면 매개 변수 이름이 부분 멤버의 선언에서 일치하는지 확인하는 것이 좋습니다.

질문 열기

멤버 종류

부분 이벤트, 생성자, 연산자, 필드를 원합니까? 처음 두 멤버 종류를 제안하지만 다른 하위 집합을 고려할 수 있습니다.

부분 기본 생성자(예: 사용자가 여러 부분 형식 선언에 대해 동일한 매개 변수 목록을 가질 수 있도록 허용)도 고려할 수 있습니다.

속성 위치

[method:] 특성 대상 지정자를 부분 이벤트에 대해 인식해야 하나요, 아니면 정의 선언에 대해서만 인식해야 하나요? 그런 다음, 결과적으로 생성되는 접근자 속성은 정의된 선언 부분과 구현 선언의 접근자로부터의 자가 대상 지정 및 method-대상 지정 특성을 포함하여, method-대상을 지정하는 특성들을 모두 연결하여 구성됩니다. 다른 선언 종류의 특성을 결합하는 것은 전례가 없을 것이며 실제로 Roslyn에서 특성 일치의 현재 구현은 이를 지원하지 않습니다.

우리는 부분 이벤트뿐만 아니라 [param:][return:]와 같은 모든 필드 유사 이벤트와 외부 이벤트를 인식하는 것도 고려할 수 있습니다. 자세한 내용은 를 참조하세요 https://github.com/dotnet/roslyn/issues/77254.