다음을 통해 공유


부분 메서드 확장

메모

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

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

사양문서에서 피처 스펙릿을 C# 언어 표준에 채택하는 과정에 대해 자세히 알아볼 수 있습니다.

챔피언 이슈: https://github.com/dotnet/csharplang/issues/3301

요약

이 제안은 C#에서 partial 메서드의 서명과 관련하여 모든 제한을 제거하는 것을 목표로 합니다. 목표는 이러한 메서드가 원본 생성기에서 작동할 수 있는 시나리오 집합을 확장하고 C# 메서드에 대한 보다 일반적인 선언 양식이 되는 것입니다.

원래 부분 메서드 사양(§15.6.9)도 참조하세요.

동기

C#은 메서드를 선언 및 정의/구현으로 분할하는 개발자를 위한 제한된 지원을 제공합니다.

partial class C
{
    // The declaration of C.M
    partial void M(string message);
}

partial class C
{
    // The definition of C.M
    partial void M(string message) => Console.WriteLine(message);
}

partial 메서드의 한 가지 동작은 정의가 없을 때 언어가 partial 메서드에 대한 호출을 지우는 것입니다. 기본적으로 조건이 false로 평가된 [Conditional] 메서드에 대한 호출처럼 동작합니다.

partial class D
{
    partial void M(string message);

    void Example()
    {
        M(GetIt()); // Call to M and GetIt erased at compile time
    }

    string GetIt() => "Hello World";
}

이 기능의 원래 동기는 디자이너 생성 코드 형식의 소스 생성이었습니다. 사용자는 생성된 코드의 일부 측면을 연결하려고 했기 때문에 생성된 코드를 지속적으로 편집하고 있었습니다. 특히 구성 요소가 초기화된 후 Windows Forms 시작 프로세스의 일부입니다.

디자이너가 코드를 다시 생성하게 한 작업으로 인해 사용자 편집이 지워지므로 생성된 코드를 편집할 때 오류가 발생하기 쉽습니다. partial 메서드 기능은 디자이너가 partial 메서드의 형태로 후크를 내보낸 것을 허용했기 때문에 이러한 긴장을 완화시켰습니다.

디자이너는 partial void OnComponentInit()과 같은 후크를 방출할 수 있고, 개발자는 이를 위한 선언을 정의하거나 정의하지 않을 수 있습니다. 두 경우 중 어느 경우라도, 생성된 코드가 컴파일될 것이며, 프로세스에 관심이 있는 개발자는 필요에 따라 참여할 수 있습니다.

이는 부분 메서드에 다음과 같은 몇 가지 제한 사항이 있음을 의미합니다.

  1. 반환 형식으로 void를 사용해야 합니다.
  2. out 매개 변수를 사용할 수 없습니다.
  3. 접근성(암시적으로 private)을 가질 수 없습니다.

이러한 제한은 호출 사이트가 지워질 때 언어가 코드를 내보낼 수 있어야 하기 때문에 존재합니다. 멤버가 어셈블리 메타데이터에서 노출될 수 없기 때문에, 지울 수 있는 유일한 접근 수준은 private입니다. 또한 이러한 제한은 partial 메서드를 적용할 수 있는 시나리오 집합을 제한하는 역할을 합니다.

여기서 제안하는 것은 partial 메서드와 관련된 기존 제한을 모두 제거하는 것입니다. 기본적으로 out 매개변수, 무효가 아닌 반환 유형 또는 모든 접근 유형을 허용합니다. 그런 다음 이러한 partial 선언에는 정의가 있어야 한다는 추가 요구 사항이 있습니다. 즉, 언어는 통화 사이트를 지우는 영향을 고려할 필요가 없습니다.

이렇게 하면 partial 메서드가 참여할 수 있는 생성기 시나리오 집합이 확장되므로 원본 생성기 기능과 잘 연결됩니다. 예를 들어 다음 패턴을 사용하여 regex를 정의할 수 있습니다.

[RegexGenerated("(dog|cat|fish)")]
partial bool IsPetMatch(string input);

이를 통해 개발자는 생성기를 옵트인하는 간단한 선언적 방법을 제공할 뿐만 아니라 생성된 출력을 구동하기 위해 소스 코드에서 살펴볼 수 있는 매우 쉬운 선언 집합을 생성기에 제공합니다.

이를 생성기에서 다음 코드 조각을 연결해야 하는 어려움과 비교합니다.

var regex = new RegularExpression("(dog|cat|fish)");
if (regex.IsMatch(someInput))
{

}

컴파일러가 생성기가 이 패턴을 적용하도록 코드를 수정하는 것을 허용하지 않기 때문에, 생성기가 이를 수행하는 것은 사실상 불가능합니다. IsMatch 구현에서 리플렉션을 사용하거나 사용자에게 호출 사이트를 새 메서드로 변경하도록 요청하거나 문자열 리터럴을 인수로 전달하도록 regex를 리팩터링해야 합니다. 그것은 꽤 지저분합니다.

상세 디자인

언어는 명시적 접근성 한정자를 사용하여 partial 메서드에 주석을 추가할 수 있도록 변경됩니다. 즉, private, public등으로 레이블을 지정할 수 있습니다.

partial 메서드에 명시적 접근성 한정자가 있는 경우 언어는 접근성이 private경우에도 선언에 일치하는 정의가 있어야 합니다.

partial class C
{
    // Okay because no definition is required here
    partial void M1();

    // Okay because M2 has a definition
    private partial void M2();

    // Error: partial method M3 must have a definition
    private partial void M3();
}

partial class C
{
    private partial void M2() { }
}

또한 언어는 명시적 접근성이 있는 partial 메서드에 나타날 수 있는 항목에 대한 모든 제한을 제거합니다. 이러한 선언에는 void가 아닌 반환 형식, out 매개 변수, extern 한정자 등이 포함될 수 있습니다. 이러한 서명은 C# 언어의 전체 표현성을 갖습니다.

partial class D
{
    // Okay
    internal partial bool TryParse(string s, out int i); 
}

partial class D
{
    internal partial bool TryParse(string s, out int i) { ... }
}

이렇게 하면 partial 메서드가 overridesinterface 구현에 참여할 수 있습니다.

interface IStudent
{
    string GetName();
}

partial class C : IStudent
{
    public virtual partial string GetName(); 
}

partial class C
{
    public virtual partial string GetName() => "Jarde";
}

컴파일러는 partial 메서드에 잘못된 요소가 포함될 때, 그에 따라 발생하는 오류 메시지를 기본적으로 다음과 같이 변경합니다.

명시적 접근성이 없는 ref 메서드에서 partial 사용할 수 없음

이렇게 하면 개발자가 이 기능을 사용할 때 올바른 방향으로 안내할 수 있습니다.

제한:

  • 명시적 접근성이 있는 partial 선언에는 정의가 있어야 합니다.
  • partial 선언 및 정의 서명은 모든 메서드 및 매개 변수 한정자에서 일치해야 합니다. 매개 변수 이름 및 특성 목록만 다를 수 있습니다(새 항목이 아니라 partial 메서드의 기존 요구 사항).

질문

모든 멤버가 부분적으로 포함됨

원본 생성기에 더 친숙하도록 partial 확장하고 있다는 점을 감안할 때 모든 클래스 멤버에서 작동하도록 확장해야 하나요? 예를 들어 partial 생성자, 연산자 등을 선언할 수 있어야 합니다.

결의 아이디어는 건전하지만, C# 9 일정의 이 시점에서는 불필요한 기능 확장을 피하려고 하고 있습니다. 최신 원본 생성기에서 작동하도록 기능을 확장하는 즉각적인 문제를 해결하려고 합니다.

다른 멤버를 지원하도록 partial 확장하는 것은 C# 10 릴리스에 고려됩니다. 이 확장을 고려할 것 같습니다. 이것은 활성 제안으로 남아 있지만 아직 구현되지 않았습니다.

부분 대신 추상 사용

이 제안의 핵심은 기본적으로 선언에 해당 정의/구현이 있는지 확인합니다. 이미 개발자가 구현에 대해 생각하도록 강제하는 언어 키워드이므로 abstract 사용해야 하나요?

결의안 이에 대한 건전한 논의가 있었지만 결국 반대로 결정되었습니다. 예, 요구 사항은 친숙하지만 개념은 크게 다릅니다. 가상 슬롯을 만들고 있지 않을 때 개발자가 가상 슬롯을 만들고 있다고 쉽게 믿게 할 수 있습니다.