다음을 통해 공유


오버로드 해결 우선 순위

메모

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

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

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

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

요약

API는 일반적으로 모호한 것으로 간주되거나 C#의 오버로드 확인 규칙에 의해 선택되지 않더라도 API 소비자가 특정 API를 사용하도록 API 소비자를 조종하는 수단으로 단일 형식 내에서 오버로드의 상대적 우선 순위를 조정하기 위해 API 작성자가 사용할 수 있는 System.Runtime.CompilerServices.OverloadResolutionPriority새 특성을 소개합니다.

동기

API 작성자는 멤버가 더 이상 권장되지 않을 때 어떻게 처리해야 할지에 대한 문제에 자주 직면합니다. 이전 버전과의 호환성을 유지하기 위해, 많은 사용자가 ObsoleteAttribute를 오류로 설정된 상태로 기존 멤버를 영구히 유지하여 런타임에서 소비자들이 이진 파일을 업그레이드할 때 문제가 발생하지 않도록 합니다. 플러그 인 작성자가 플러그 인이 실행되는 환경을 제어하지 않는 플러그 인 시스템에 특히 해당합니다. 환경의 작성자는 이전 메서드를 유지하지만 새로 개발된 코드에 대한 액세스를 차단할 수 있습니다. 그러나 ObsoleteAttribute 그 자체로는 충분하지 않습니다. 형식 또는 멤버가 여전히 오버로드 해석에서 표시되어, 완벽한 대안이 있을 경우에도 원치 않는 오버로드 해석 실패를 일으킬 수 있습니다. 이런 대안은 사용되지 않는 멤버와 모호한 경우가 있거나, 사용되지 않는 멤버로 인해 좋은 멤버를 전혀 고려하지 않고 해석 과정이 조기에 종료되는 원인이 될 수 있습니다. 이를 위해 API 작성자가 모호성 해결에 대한 오버로드 해결을 안내하여 API 노출 영역을 발전시키고 사용자 환경을 손상시키지 않고도 성능이 좋은 API로 사용자를 안내할 수 있는 방법을 원합니다.

BCL(기본 클래스 라이브러리) 팀에는 유용할 수 있는 몇 가지 예가 있습니다. 일부(가상) 예제는 다음과 같습니다.

  • Debug.Assert을 사용하여 어설션되는 식을 가져오고 메시지에 포함할 수 있도록 하는 CallerArgumentExpression 오버로드를 생성하여, 기존 오버로드보다 더 선호되도록 합니다.
  • string.IndexOf(string, StringComparison = Ordinal)을(를) string.IndexOf(string)보다 더 선호하게 만들기. 이는 잠재적인 호환성 손상 변경으로 간주될 수 있지만, 더 나은 기본값이며 사용자가 의도한 것일 가능성이 더 높다고 생각되는 경우도 있습니다.
  • 이 제안과 CallerAssemblyAttribute 조합하면 암시적 호출자 ID가 있는 메서드가 비용이 많이 드는 스택 워크를 방지할 수 있습니다. Assembly.Load(AssemblyName)는 오늘 이 작업을 수행하며, 훨씬 더 효율적일 수 있습니다.
  • Microsoft.Extensions.Primitives.StringValuesstringstring[]모두에 대한 암시적 변환을 노출합니다. 즉, params string[]params ReadOnlySpan<string> 오버로드가 모두 있는 메서드에 전달될 때 모호합니다. 이 특성은 모호성을 방지하기 위해 오버로드 중 하나의 우선 순위를 지정하는 데 사용할 수 있습니다.

상세 디자인

오버로드 해결 우선 순위

우리는 메서드 그룹을 확인하는 과정에서 사용되는 새로운 개념인 overload_resolution_priority을 정의합니다. overload_resolution_priority 32비트 정수 값입니다. 모든 메서드는 기본적으로 overload_resolution_priority 값이 0으로 설정되어 있으며, 이는 메서드에 OverloadResolutionPriorityAttribute를 적용하여 변경할 수 있습니다. C# 사양의 §12.6.4.1 섹션을 다음과 같이 업데이트합니다(굵게변경).

후보 함수 멤버와 인수 목록이 식별되면 최상의 함수 멤버의 선택은 모든 경우에 동일합니다.

  • 첫째, 후보 함수 멤버 집합은 지정된 인수 목록(§12.6.4.2)과 관련하여 적용 가능한 함수 멤버로 축소됩니다. 이 축소된 집합이 비어 있으면 컴파일 시간 오류가 발생합니다.
  • 그런 다음, 줄어든 후보 멤버 집합은 선언 형식에 따라 그룹화됩니다. 각 그룹 내에서:
    • 후보 함수 멤버는 오버로드 해결 우선순위에 따라 정렬됩니다. 멤버가 재정의인 경우, overload_resolution_priority는 해당 멤버의 가장 덜 파생된 선언에서 가져옵니다.
    • 선언 형식 그룹 내에서 최상위 수준의 overload_resolution_priority 보다 낮은 모든 멤버가 제거됩니다.
  • 축소된 그룹은 해당 후보 함수 멤버의 최종 집합으로 다시 결합됩니다.
  • 그런 다음, 적용 가능한 후보 함수 멤버 집합에서 가장 적합한 함수 멤버를 찾습니다. 집합에 하나의 함수 멤버만 포함된 경우 해당 함수 멤버가 가장 적합한 함수 멤버입니다. 그렇지 않으면 각 함수 멤버가 §12.6.4.3규칙을 사용하여 다른 모든 함수 멤버와 비교되는 경우 지정된 인수 목록과 관련하여 다른 모든 함수 멤버보다 더 나은 함수 멤버가 가장 좋습니다. 다른 모든 함수 멤버보다 더 나은 함수 멤버가 하나도 없는 경우 함수 멤버 호출이 모호하고 바인딩 시간 오류가 발생합니다.

예를 들어, 이 기능을 사용하면 다음 코드 조각이 "배열"이 아닌 "Span"을 출력합니다.

using System.Runtime.CompilerServices;

var d = new C1();
int[] arr = [1, 2, 3];
d.M(arr); // Prints "Span"

class C1
{
    [OverloadResolutionPriority(1)]
    public void M(ReadOnlySpan<int> s) => Console.WriteLine("Span");
    // Default overload resolution priority
    public void M(int[] a) => Console.WriteLine("Array");
}

이 변경의 효과는 대부분의 파생 형식에 대한 정리와 마찬가지로 오버로드 확인 우선 순위에 대한 최종 정리를 추가한다는 것입니다. 이 가지치기는 오버로드 해석 프로세스의 최종 단계에서 발생하므로, 기본 형식이 파생 형식보다 멤버의 우선 순위를 높일 수 없다는 것을 의미합니다. 이는 의도적인 것이며 기본 형식이 파생 형식보다 항상 더 나은 것을 시도할 수 있는 군비 경합이 발생하지 않도록 방지합니다. 예를 들어:

using System.Runtime.CompilerServices;

var d = new Derived();
d.M([1, 2, 3]); // Prints "Derived", because members from Base are not considered due to finding an applicable member in Derived

class Base
{
    [OverloadResolutionPriority(1)]
    public void M(ReadOnlySpan<int> s) => Console.WriteLine("Base");
}

class Derived : Base
{
    public void M(int[] a) => Console.WriteLine("Derived");
}

음수는 사용할 수 있으며 특정 오버로드를 다른 모든 기본 오버로드보다 더 나쁜 것으로 표시하는 데 사용할 수 있습니다.

멤버의 overload_resolution_priority 해당 멤버의 최소 파생 선언에서 가져옵니다. overload_resolution_priority는 형식 멤버가 구현할 수 있는 어떤 인터페이스 멤버에서도 상속되거나 유추되지 않으며, 인터페이스 멤버 Mx을(를) 구현하는 멤버 Mi가 주어질 때 MxMi가 서로 다른 overload_resolution_priorities를 가질 경우에도 경고가 발생하지 않습니다.

NB: 이 규칙의 의도는 params 한정자의 동작을 복제하는 것입니다.

System.Runtime.CompilerServices.OverloadResolutionPriorityAttribute

BCL에는 다음 특성이 도입되었습니다.

namespace System.Runtime.CompilerServices;

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
public sealed class OverloadResolutionPriorityAttribute(int priority) : Attribute
{
    public int Priority => priority;
}

C#의 모든 메서드는 특성이 지정되지 않은 경우, 기본 OverloadResolutionPriorityAttribute 값은 0입니다. 그 특성이 그들에게 할당된 경우, 해당 overload_resolution_priority는 그 특성의 첫 번째 인수에 제공된 정수 값입니다.

다음 위치에 OverloadResolutionPriorityAttribute 적용하는 것은 오류입니다.

  • 인덱서가 아닌 속성
  • 속성, 인덱서 또는 이벤트 접근자
  • 변환 연산자
  • 람다
  • 로컬 함수
  • 종료자
  • 정적 생성자

메타데이터에서 이러한 위치에서 발생하는 특성은 C#에서 무시됩니다.

우선 순위가 멤버의 가장 덜 파생된 선언에서 읽히기 때문에, 기본 메서드를 재정의하는 곳과 같이 무시될 사항에 OverloadResolutionPriorityAttribute을 사용하면 오류가 발생합니다.

NB: 이는 무시될 때 다시 지정하거나 추가할 수 있는 params 한정자의 동작과 의도적으로 다릅니다.

멤버의 호출 가능성

OverloadResolutionPriorityAttribute에 대한 중요한 주의 사항은, 이것이 특정 멤버를 소스 코드에서 실질적으로 호출할 수 없게 만들 수 있다는 것입니다. 예를 들어:

using System.Runtime.CompilerServices;

int i = 1;
var c = new C3();
c.M1(i); // Will call C3.M1(long), even though there's an identity conversion for M1(int)
c.M2(i); // Will call C3.M2(int, string), even though C3.M1(int) has less default parameters

class C3
{
    public void M1(int i) {}
    [OverloadResolutionPriority(1)]
    public void M1(long l) {}

    [Conditional("DEBUG")]
    public void M2(int i) {}
    [OverloadResolutionPriority(1), Conditional("DEBUG")]
    public void M2(int i, [CallerArgumentExpression(nameof(i))] string s = "") {}

    public void M3(string s) {}
    [OverloadResolutionPriority(1)]
    public void M3(object o) {}
}

이러한 예제의 경우 기본 우선 순위 오버로드는 효과적으로 명목상으로만 남게 되며, 몇 가지 추가 작업을 거쳐야만 호출할 수 있습니다.

  • 메서드를 대리자로 변환한 다음 해당 대리자를 사용합니다.
    • M3(object) 우선 순위가 지정된 M3(string)같은 일부 참조 형식 분산 시나리오의 경우, 이 전략은 실패할 것입니다.
    • 조건부 메서드를 대리자로 변환할 수 없으므로 M2같은 조건부 메서드도 이 전략으로 호출할 수 없습니다.
  • UnsafeAccessor 런타임 기능을 사용하여 일치하는 서명을 통해 호출합니다.
  • 수동으로 리플렉션을 사용하여 메서드에 대한 참조를 얻고, 이를 호출하는 과정입니다.
  • 다시 컴파일되지 않은 코드는 계속해서 이전 메서드를 호출합니다.
  • 필기 IL은 선택한 내용을 지정할 수 있습니다.

질문 열기

확장 메서드 그룹화(응답됨)

현재 문구에 따르면, 확장 메서드는 자체 유형내에서만 우선 순위 에 따라 정렬됩니다. 예를 들어:

new C2().M([1, 2, 3]); // Will print Ext2 ReadOnlySpan

static class Ext1
{
    [OverloadResolutionPriority(1)]
    public static void M(this C2 c, Span<int> s) => Console.WriteLine("Ext1 Span");
    [OverloadResolutionPriority(0)]
    public static void M(this C2 c, ReadOnlySpan<int> s) => Console.WriteLine("Ext1 ReadOnlySpan");
}

static class Ext2
{
    [OverloadResolutionPriority(0)]
    public static void M(this C2 c, ReadOnlySpan<int> s) => Console.WriteLine("Ext2 ReadOnlySpan");
}

class C2 {}

확장 멤버에 대해 오버로드 확인을 수행할 때 형식을 선언하여 정렬하지 말고 대신 동일한 범위 내의 모든 확장을 고려해야 하나요?

대답

우리는 항상 그룹화할 것입니다. 위의 예제에서는 Ext2 ReadOnlySpan 인쇄합니다.

오버라이드에 대한 속성 상속(답변됨)

특성을 상속해야 하나요? 그렇지 않은 경우 재정의 멤버의 우선 순위는 무엇인가요?
가상 멤버에 특성을 지정하는 경우 특성을 반복하려면 해당 멤버의 재정의가 필요합니까?

대답

특성은 상속된 것으로 표시되지 않습니다. 멤버의 최소 파생 선언을 살펴보고 오버로드 확인 우선 순위를 결정합니다.

오버라이드 시 발생하는 애플리케이션 오류 또는 경고 (답변 완료)

class Base
{
    [OverloadResolutionPriority(1)] public virtual void M() {}
}
class Derived
{
    [OverloadResolutionPriority(2)] public override void M() {} // Warn or error for the useless and ignored attribute?
}

OverloadResolutionPriorityAttribute이 무시되는 경우, 예를 들어 재정의할 때 어떻게 적용해야 할까요?

  1. 아무것도 하지 마세요, 조용히 무시되도록 하세요.
  2. 특성이 무시된다는 경고를 실행합니다.
  3. 특성이 허용되지 않는다는 오류를 발생시킵니다.

3은 우리가 미래에 이 특성을 지정하는 재정의를 허용할 수 있는 어떤 가능성을 생각할 때, 가장 신중한 방법입니다.

대답

우리는 3번 옵션을 선택하고, 무시될 장소에서는 애플리케이션을 차단합니다.

암시적 인터페이스 구현(응답됨)

암시적 인터페이스 구현의 동작은 무엇인가요? OverloadResolutionPriority지정해야 하나요? 우선 순위 없이 암시적 구현이 발생할 때 컴파일러의 동작은 어떻게 되나요? 인터페이스 라이브러리가 업데이트될 수 있지만 구현이 아니기 때문에 이 작업은 거의 확실하게 발생합니다. 여기서 params에 관한 선행 기술은 값을 지정하지 않고 이월하지 않는 것입니다.

using System;

var c = new C();
c.M(1, 2, 3); // error CS1501: No overload for method 'M' takes 3 arguments
((I)c).M(1, 2, 3);

interface I
{
    void M(params int[] ints);
}

class C : I
{
    public void M(int[] ints) { Console.WriteLine("params"); }
}

옵션은 다음과 같습니다.

  1. params을 따르세요. OverloadResolutionPriorityAttribute가 암시적으로 전달되거나 지정될 필요가 없습니다.
  2. 특별한 설명 없이 속성을 전달합니다.
  3. 특성을 암시적으로 이월하지 마세요. 호출 사이트에서 특성을 지정해야 합니다.
    1. 컴파일러가 컴파일된 참조를 사용하여 이 시나리오를 발견할 때 동작은 어떻게 되나요?

대답

우리는 1로 갈 것입니다.

추가 애플리케이션 오류(답변됨)

같은 확인해야 하는 몇 가지 위치가 더 있습니다. 다음을 포함합니다.

  • 변환 연산자 - 사양에서는 변환 연산자가 오버로드 확인을 거치지 않으므로 구현에서 이러한 멤버에 대한 애플리케이션을 차단합니다. 확인해야 하나요?
  • 람다 - 마찬가지로 람다는 오버로드 확인의 대상이 되지 않으므로 구현에서 차단합니다. 확인해야 하나요?
  • 소멸자 - 또한 현재 차단됨.
  • 정적 생성자 - 다시, 현재 차단됩니다.
  • 로컬 함수 - 오버로드 확인을 수행할 있기 때문에 현재 차단되지 않습니다. 오버로드를 오버로드할 수 없습니다. 이는 특성이 오버로드되지 않은 형식의 멤버에 적용될 때 오류를 발생시키지 않는 방식과 유사합니다. 이 동작을 확인해야 하나요?

대답

위에 나열된 모든 위치가 차단됩니다.

Langversion 동작(응답됨)

현재 구현은 이 적용되는 경우에만 langversion 오류를 발생시키며, 실제로 영향을 미치는 것은이 아니라 입니다. 이 결정은 BCL에서 이 특성을 사용할 API가 현재뿐만 아니라 앞으로도 추가될 것이기 때문에 내려졌습니다. 사용자가 수동으로 언어 버전을 C# 12 또는 그 이전 버전으로 설정하면 이러한 멤버가 표시될 수 있으며, 우리 langversion 동작에 따라 다음 중 하나가 나타날 수 있습니다.

  • C# <13에서 특성을 무시하면 API가 특성 없이는 모호하기 때문에 모호성 오류가 발생합니다.
  • 특성이 결과에 영향을 줄 때 오류가 발생하면 API를 실행할 수 없다는 오류가 발생합니다. .NET 9에서 Debug.Assert(bool)의 우선 순위가 낮아져서 특히 문제가 될 것입니다.
  • 해결 방법을 조용히 변경하면 한 컴파일러 버전에서는 특성을 이해하고 다른 컴파일러 버전에서는 이해하지 못하는 경우, 서로 다른 버전 간에 잠재적으로 다른 동작이 발생할 수 있습니다.

마지막 동작은 가장 정방향 호환성을 초래하기 때문에 선택되었지만 변경 결과는 일부 사용자에게 놀라운 결과를 초래할 수 있습니다. 확인해야 하나요, 아니면 다른 옵션 중 하나를 선택해야 하나요?

대답

이전 언어 버전의 특성을 자동으로 무시하고 옵션 1로 이동합니다.

대안

이전 제안은 가시성에서 항목을 제거함에 있어 지나치게 엄격한 BinaryCompatOnlyAttribute 접근법을 지정하려고 했습니다. 그러나 어려운 구현 문제로 인해 제안이 너무 강력하여 유용하지 않거나(예를 들어 이전 API 테스트를 방지한다는 점에서), 또는 너무 약하여 원래 목표 중 일부를 놓치는 경우가 많습니다(모호하게 간주될 수 있는 API가 새 API로 호출되는 경우가 그러합니다). 해당 버전은 아래에서 복제됩니다.

BinaryCompatOnlyAttribute 제안(폐기됨)

BinaryCompatOnlyAttribute

상세 디자인

System.BinaryCompatOnlyAttribute

저희는 새로운 예약 특성을 도입합니다.

namespace System;

// Excludes Assembly, GenericParameter, Module, Parameter, ReturnValue
[AttributeUsage(AttributeTargets.Class
                | AttributeTargets.Constructor
                | AttributeTargets.Delegate
                | AttributeTargets.Enum
                | AttributeTargets.Event
                | AttributeTargets.Field
                | AttributeTargets.Interface
                | AttributeTargets.Method
                | AttributeTargets.Property
                | AttributeTargets.Struct,
                AllowMultiple = false,
                Inherited = false)]
public class BinaryCompatOnlyAttribute : Attribute {}

형식 멤버에 적용되는 경우 해당 멤버는 컴파일러가 모든 위치에서 액세스할 수 없는 것으로 처리됩니다. 즉, 멤버 조회, 오버로드 확인 또는 기타 유사한 프로세스에 기여하지 않습니다.

접근성 도메인

따라 §7.5.3 접근성 도메인 업데이트합니다.

접근성 도메인는 멤버에 대한 액세스가 허용되는 프로그램 텍스트의 (겹치지 않을 수 있는) 섹션으로 구성됩니다. 멤버의 접근성 도메인을 정의하기 위해, 형식 내에 선언되지 않은 경우 최상위 멤버라고 하며, 다른 형식 내에 선언된 경우 중첩된 멤버라고 합니다. 또한, 프로그램의 프로그램 텍스트는 프로그램의 모든 컴파일 단위에 포함된 모든 텍스트로 정의됩니다. 형식의 프로그램 텍스트는 해당 형식의 type_declaration에 포함된 모든 텍스트(형식 내에 중첩된 형식을 포함할 수 있음)로 정의됩니다.

미리 정의된 형식(예: object, int또는 double)의 접근성 도메인은 무제한입니다.

프로그램 T에 선언된 최상위 언바운드 형식 (P)의 접근 가능 범위는 다음과 같이 정의되어 있습니다.

  • T BinaryCompatOnlyAttribute표시된 경우 T 접근성 도메인은 P 프로그램 텍스트 및 P참조하는 프로그램에 완전히 액세스할 수 없습니다.
  • T의 접근성이 선언된 대로 공용인 경우, T의 접근성 도메인은 P의 프로그램 텍스트 및 P을 참조하는 모든 프로그램입니다.
  • T의 접근성이 내부로 선언된 경우, T의 접근성 도메인은 P의 프로그램 텍스트입니다.

참고: 이 정의에 따르면, 최상위 제한되지 않은 형식의 접근성 도메인은 항상 해당 형식이 선언된 프로그램의 텍스트에 있습니다. 끝 메모

생성된 형식 T<A₁, ..., Aₑ> 대한 접근성 도메인은 바인딩되지 않은 제네릭 형식 T 접근성 도메인과 A₁, ..., Aₑ형식 인수의 접근성 도메인의 교집합입니다.

프로그램 M내의 형식 T에 선언된 중첩 멤버 P의 접근성 도메인은 다음과 같이 정의됩니다(M 자체가 형식일 수 있음).

  • M BinaryCompatOnlyAttribute표시된 경우 M 접근성 도메인은 P 프로그램 텍스트 및 P참조하는 프로그램에 완전히 액세스할 수 없습니다.
  • M의 선언된 접근성이 public인 경우, M의 접근성 도메인은 T의 접근성 도메인입니다.
  • M 선언된 접근성이 protected internal경우 D외부에서 선언되는 P파생된 모든 형식의 프로그램 텍스트와 T 프로그램 텍스트의 통합이 P. M 접근성 도메인은 T 접근성 도메인과 D의 교집합입니다.
  • M의 선언된 접근성이 private protected인 경우, D의 프로그램 텍스트와 P의 프로그램 텍스트, 그리고 T로부터 파생된 모든 형식의 교집합을 T로 설정하십시오. M 접근성 도메인은 T 접근성 도메인과 D의 교집합입니다.
  • 만약 M의 선언된 접근성이 protected이라면, DT의 프로그램 텍스트와 T로부터 파생된 모든 형식의 프로그램 텍스트의 합이 되도록 합니다. M 접근성 도메인은 T 접근성 도메인과 D의 교집합입니다.
  • 만약 M의 선언된 접근성이 internal인 경우, M의 접근성 도메인은 T의 접근성 도메인과 P의 프로그램 텍스트의 교집합입니다.
  • 만약 M의 선언된 접근성이 private이라면, M의 접근성 도메인은 T의 프로그램 텍스트입니다.

이러한 추가의 목표는 BinaryCompatOnlyAttribute 표시된 멤버가 모든 위치에 완전히 액세스할 수 없고, 멤버 조회에 참여하지 않으며, 프로그램의 나머지 부분에 영향을 줄 수 없도록 하는 것입니다. 따라서 이는 인터페이스 멤버를 구현할 수 없고, 서로 호출할 수 없으며, 재정의(가상 메서드), 숨기거나 구현할 수 없음(인터페이스 멤버)을 의미합니다. 이것이 너무 엄격한지 여부는 아래의 몇 가지 공개 질문의 주제입니다.

해결되지 않은 질문

가상 메서드 및 재정의

가상 메서드가 BinaryCompatOnly표시되면 어떻게 해야 할까요? 파생 클래스의 오버라이드는 현재 어셈블리에 포함되지 않을 수 있으며, 사용자가 C#에서 일반적으로 오버로드를 허용하지 않는 반환 형식에 따라 메서드의 새 버전을 도입하려고 할 수도 있습니다. 다시 컴파일할 때 이전 메서드를 오버라이드한 부분은 어떻게 되나요? 그들이 BinaryCompatOnly로 표시된 경우에도 BinaryCompatOnly 멤버를 재정의할 수 있나요?

동일한 DLL 내에서 사용

이 제안에 따르면 BinaryCompatOnly 멤버는 현재 컴파일 중인 어셈블리에서도 표시되지 않습니다. 너무 엄격한가요, 아니면 BinaryCompatAttribute 구성원들이 서로 연결될 필요가 있나요?

암시적으로 인터페이스 멤버 구현

BinaryCompatOnly 멤버가 인터페이스 멤버를 구현할 수 있어야 하나요? 그들이 그렇게 하지 못하도록 막아야 할까요? 사용자가 암시적 인터페이스 구현을 BinaryCompatOnly로 전환하려는 경우, 명시적 인터페이스 구현을 추가로 제공해야 하며, 이 구현은 BinaryCompatOnly 멤버와 동일한 본문을 복제해야 합니다. 그렇지 않으면 원래 멤버를 더 이상 볼 수 없게 될 것입니다.

BinaryCompatOnly로 표시된 인터페이스 멤버를 구현하기

인터페이스 멤버가 BinaryCompatOnly표시되면 어떻게 해야 할까요? 형식은 여전히 해당 멤버에 대한 구현을 제공해야 합니다. 우리는 인터페이스 멤버를 BinaryCompatOnly로 표시할 수 없다고 말해야 할 수도 있습니다.