모호성에 관여하지 않는 경우 메서드를 추가하면 모호한 호출이 추가되는 이유


112

이 수업이 있습니다

public class Overloaded
{
    public void ComplexOverloadResolution(params string[] something)
    {
        Console.WriteLine("Normal Winner");
    }

    public void ComplexOverloadResolution<M>(M something)
    {
        Console.WriteLine("Confused");
    }
}

이렇게 부르면 :

        var blah = new Overloaded();
        blah.ComplexOverloadResolution("Which wins?");

Normal Winner콘솔에 기록 합니다.

그러나 다른 방법을 추가하면 :

    public void ComplexOverloadResolution(string something, object somethingElse = null)
    {
        Console.WriteLine("Added Later");
    }

다음과 같은 오류가 발생합니다.

다음 메서드 또는 속성 간의 호출이 모호합니다.> ' Overloaded.ComplexOverloadResolution(params string[])'및 ' Overloaded.ComplexOverloadResolution<string>(string)'

메서드를 추가하면 호출 모호성이 발생할 수 있지만 이미 존재하는 두 메서드 (params string[])<string>(string)! 첫 번째는 매개 변수이고 두 번째는 제네릭이기 때문에 모호성과 관련된 두 가지 방법 중 어느 것도 새로 추가 된 방법이 아닙니다.

이것은 버그입니까? 사양의 어떤 부분이 이것이 사실이어야한다고 말합니까?


2
나는 방법을 'Overloaded.ComplexOverloadResolution(string)'언급 한다고 생각하지 않는다 <string>(string). (string, object)제공된 개체가없는 메서드를 참조한다고 생각합니다 .
phoog

1
@phoog 오, 그 데이터는 태그이기 때문에 StackOverflow에 의해 잘 렸지만 오류 메시지에는 템플릿 지정자가 있습니다. 나는 그것을 다시 추가 해요.
맥케이

날 잡았어! 나는 내 대답에서 사양의 관련 섹션을 인용했지만 지난 30 분 동안 읽고 이해하지 못했습니다!
phoog dec

@phoog, 사양의 해당 부분을 살펴보면 다른 두 가지 방법이 아닌 자체 및 다른 방법 이외의 방법에 모호성을 도입하는 것에 대해 아무것도 볼 수 없습니다.
McKay

이것은 가위 바위 보일 뿐이라는 생각이 들었습니다 . 두 개의 고유 한 값 세트가 승자가 있지만 세 가지 값의 완전한 세트는 그렇지 않습니다.
phoog

답변:


107

이것은 버그입니까?

예.

축하합니다. 과부하 해결에서 버그를 발견했습니다. 버그는 C # 4 및 5에서 재현됩니다. 의미 분석기의 "Roslyn"버전에서는 재현되지 않습니다. C # 5 테스트 팀에 알 렸으며 최종 릴리스 전에이 문제를 조사하고 해결할 수 있기를 바랍니다. (항상 그렇듯이 약속은 없습니다.)

올바른 분석이 이어집니다. 후보자는 다음과 같습니다.

0: C(params string[]) in its normal form
1: C(params string[]) in its expanded form
2: C<string>(string) 
3: C(string, object) 

후보 0은로 string변환 할 수 없기 때문에 분명히 적용 할 수 없습니다 string[]. 3 개 남았습니다.

세 가지 중 고유 한 최선의 방법을 결정해야합니다. 나머지 세 후보를 쌍으로 비교하여이를 수행합니다. 그러한 세 쌍이 있습니다. 생략 된 선택적 매개 변수를 제거 하면 모두 동일한 매개 변수 목록을 가지므로 사양의 섹션 7.5.3.2에 설명 된 고급 타이 브레이킹 라운드로 이동해야합니다.

어느 쪽이 더 낫습니까, 1 또는 2? 관련 순위 결정자는 제네릭 메서드가 항상 비 제네릭 메서드보다 나쁘다는 것입니다. 2는 1보다 나쁩니다. 따라서 2는 승자가 될 수 없습니다.

1 또는 3 중 어느 것이 더 낫습니까? 관련 순위 결정자는 다음과 같습니다. 확장 된 형태로만 적용 할 수있는 방법은 항상 일반 형태로 적용 할 수있는 방법보다 나쁩니다. 따라서 1은 3보다 나쁩니다. 따라서 1이 승자가 될 수 없습니다.

2 개 또는 3 개 중 어느 것이 더 낫습니까? 관련 순위 결정자는 제네릭 메서드가 항상 비 제네릭 메서드보다 나쁘다는 것입니다. 2는 3보다 나쁩니다. 따라서 2는 승자가 될 수 없습니다.

여러 적용 가능한 후보자 세트에서 선택 되려면 후보자는 (1) 무패, (2) 적어도 한 명의 다른 후보를 이기고, (3) 처음 두 가지 속성을 가진 고유 한 후보 여야합니다. 후보 3은 다른 후보에 의해 구타를 당하지 않고 적어도 한 명의 다른 후보를 이깁니다. 이 속성을 가진 유일한 후보입니다. 따라서 후보 3은 고유 한 최상의 후보 입니다. 이길 것입니다.

C # 4 컴파일러가 잘못된 오류 메시지를 표시 할뿐만 아니라 기괴한 오류 메시지를보고하고 있음을 올바르게 알 수 있습니다. 컴파일러가 과부하 해결 분석을 잘못 받고 있다는 것은 조금 놀랍습니다. 오류 메시지가 잘못 표시되는 것은 전혀 놀라운 일이 아닙니다. "모호한 방법"오류 휴리스틱은 기본적으로 최상의 방법을 결정할 수없는 경우 후보 집합에서 두 가지 방법을 선택합니다. "실제"모호함을 찾는 데는 그다지 좋지 않습니다.

그 이유를 합리적으로 물을 수 있습니다. 찾기가 매우 까다 롭습니다 은 "betterness"관계이기 때문에 "unambigously 모호한"있는 방법을 자동사 . 후보 1이 2보다 낫고, 2가 3보다 낫고, 3이 1보다 낫다는 상황을 생각 해낼 수 있습니다. 그런 상황에서 우리는 그들 중 두 개를 "모호한 것"으로 선택하는 것보다 더 잘할 수 없습니다.

Roslyn에 대한이 휴리스틱을 개선하고 싶지만 우선 순위가 낮습니다.

(독자에게 연습하십시오. "선형 시간 알고리즘을 개발하여 더 나은 관계가 전 이적이지 않은 n 개의 요소 집합에서 고유 한 최상의 멤버를 식별하십시오"는이 팀을 위해 인터뷰 한 날 제가받은 질문 중 하나였습니다. 매우 어려운 알고리즘입니다. 한번 시도해보세요.)

C #에 선택적 인수를 추가하는 것을 너무 오랫동안 밀어 붙인 이유 중 하나는 과부하 해결 알고리즘에 도입되는 복잡한 모호한 상황의 수가 많기 때문입니다. 분명히 우리는 그것을 제대로 이해하지 못했습니다.

연결 문제를 입력하여 추적하고 싶다면 부담없이 사용하십시오. 우리의 관심을 끌고 싶다면 완료 한 것으로 간주하십시오. 내년에 테스트를 진행하겠습니다.

관심을 가져 주셔서 감사합니다. 오류에 대해 사과드립니다.


1
응답 해 주셔서 감사합니다. 당신은 "1은 2보다 나쁘다"라고 말했지만 방법 1과 2가 있으면 방법 1을 선택합니까?
McKay

@McKay : 아, 당신 말이 맞아요. 텍스트를 수정하겠습니다.
Eric Lippert 2011


2
@BoltClock은 실제로 "일년의 나머지 기간 동안 떠나는 것"이라는 말은 하루 쉬는 것을 의미합니다.
phoog

1
나도 그렇게 생각해. 나는 "3) 처음 두 가지 속성을 가진 유일한 후보가 되라""(무패이고 적어도 하나의 다른 후보를이기는) 유일한 후보가 되십시오 " 라고 읽었습니다 . 그러나 귀하의 가장 최근의 의견은 "(무패 한 유일한 후보가되고) 적어도 한 명의 다른 후보를 이깁니다" 라고 생각하게합니다 . 영어는 실제로 그룹화 기호를 사용할 수 있습니다. 후자가 사실이라면 나는 그것을 다시 얻습니다.
default.kramer

5

사양의 어떤 부분이 이것이 사실이어야한다고 말합니까?

섹션 7.4 (멤버 조회) 및 7.5.2 (유형 유추)와 함께 섹션 7.5.3 (오버로드 해결).

특히 섹션 7.5.3.2 (더 나은 함수 멤버)에는 부분적으로 "해당 인수가없는 선택적 매개 변수가 매개 변수 목록에서 제거됩니다."라는 내용과 "M (p)가 제네릭이 아닌 메소드 인 경우 amd M (q)가 일반적인 방법이라면 M (p)가 M (q)보다 낫습니다. "

그러나 사양의 어느 부분이이 동작을 제어하는지 알 수있을만큼 사양의 이러한 부분을 충분히 이해하지 못하며 준수 여부를 판단 할 수는 없습니다.


그러나 그것은 멤버를 추가하는 것이 이미 존재하는 두 메서드 사이에 모호성을 유발하는 이유를 설명하지 않습니다.
McKay

@McKay 충분히 공정합니다 (편집 참조). > - : 우리는 단지 에릭 Lippert의이 올바른 행동인지를 우리에게 얘기를 기다려야 할 것이다
phoog

1
그것들은 사양의 올바른 부분입니다. 문제는 그들이 이것이 사실 이어서 는 안된다고 말하는 것입니다 !
Eric Lippert 2011

3

일부 메소드에서 첫 번째 매개 변수의 이름을 변경하고 할당하려는 매개 변수를 지정하여 이러한 모호함을 피할 수 있습니다.

이렇게 :

public class Overloaded
{
    public void ComplexOverloadResolution(params string[] somethings)
    {
        Console.WriteLine("Normal Winner");
    }

    public void ComplexOverloadResolution<M>(M something)
    {
        Console.WriteLine("Confused");
    }

    public void ComplexOverloadResolution(string something, object somethingElse = null)
    {
        Console.WriteLine("Added Later");
    }
}

class Program
{
    static void Main(string[] args)
    {
        Overloaded a = new Overloaded();
        a.ComplexOverloadResolution(something:"asd");
    }
}

오,이 코드가 나쁘다는 것을 알고 있으며이를 해결하는 여러 가지 방법이 있습니다. 질문은 "왜 컴파일러가 이런 방식으로 작동합니까?"였습니다.
McKay

1

params첫 번째 방법에서 를 제거하면 이런 일이 발생하지 않습니다. 첫 번째와 세 번째 방법은 모두 유효한 호출을 가지고 ComplexOverloadResolution(string)있지만 첫 번째 방법이public void ComplexOverloadResolution(string[] something) 모호성이 없습니다.

매개 변수 값 제공 object somethingElse = null 하면 선택적인 매개 변수가되므로 해당 오버로드를 호출 할 때 지정할 필요가 없습니다.

편집 : 컴파일러는 여기에서 미친 짓을하고 있습니다. 첫 번째 메서드 이후에 코드에서 세 번째 메서드를 이동하면 올바르게보고됩니다. 따라서 올바른 항목을 확인하지 않고 처음 두 개의 과부하를 가져와보고하는 것 같습니다.

'ConsoleApplication1.Program.ComplexOverloadResolution (params string [])'및 'ConsoleApplication1.Program.ComplexOverloadResolution (string, object)'

Edit2 : 새로운 발견. 위의 세 가지 방법을 제거하면 둘 사이에 모호성이 생성되지 않습니다. 따라서 순서에 관계없이 세 가지 방법이있는 경우에만 충돌이 나타나는 것 같습니다.


그러나 그것은 멤버를 추가하는 것이 이미 존재하는 두 메서드 사이에 모호성을 유발하는 이유를 설명하지 않습니다.
McKay

첫 번째와 세 번째 방법 사이에 모호성이 발생하지만 컴파일러가 다른 두 가지 방법이 나를 넘어서는 이유를보고하는 이유는 무엇입니까?
Tomislav Markovski 2011

그러나 두 번째 메서드를 제거하면 모호하지 않고 세 번째 메서드를 성공적으로 호출합니다. 따라서 컴파일러가 첫 번째 메서드와 세 번째 메서드 사이에 모호성이없는 것 같습니다.
McKay

내 편집을 참조하십시오. 미친 컴파일러.
Tomislav Markovski 2011

실제로 두 가지 방법 만 조합하면 모호함이 없습니다. 이것은 매우 이상합니다. 편집 2.
Tomislav Markovski 2011

1
  1. 쓰면

    var blah = new Overloaded();
    blah.ComplexOverloadResolution("Which wins?");

    또는 그냥 쓰세요

    var blah = new Overloaded();
    blah.ComplexOverloadResolution();

    윌 끝 과 동일한 방법에 있어서의,

    public void ComplexOverloadResolution(params string[] something

    이것은 매개 변수가 지정되지 않은params 경우에도 가장 잘 일치하는 원인 키워드입니다.

  2. 이렇게 새로운 방법을 추가하려고한다면

    public void ComplexOverloadResolution(string something)
    {
        Console.WriteLine("Added Later");
    }

    매개 변수 가있는 호출과 완벽하게 일치하므로이 메소드를 완벽하게 컴파일하고 호출합니다 string. 다음 훨씬 더 강해 params string[] something.

  3. 두 번째 방법을 선언합니다.

    public void ComplexOverloadResolution(string something, object something=null);

    컴파일러, 첫 번째 방법과 이것 사이에 완전히 혼란스러워서 방금 추가했습니다. 그가 지금 당신의 전화에 어떤 기능을해야하는지 알지 못 하거든

    var blah = new Overloaded();
    blah.ComplexOverloadResolution("Which wins?");

    실제로 다음 코드와 같이 호출에서 문자열 매개 변수를 제거하면 모든 것이 올바르게 컴파일되고 이전처럼 작동합니다.

    var blah = new Overloaded();
    blah.ComplexOverloadResolution(); // will be ComplexOverloadResolution(params string[] something) function called here, like a best match.

먼저 동일한 두 가지 호출 사례를 작성합니다. 당연히 같은 방법으로 진행될 것입니까, 아니면 다른 것을 작성하려고 했습니까?
McKay

그러나 다시 말하지만, 나머지 대답을 올바르게 이해하고 있다면 컴파일러가 말하는 혼란, 즉 방금 추가 한 세 번째 방법이 아닌 첫 번째 방법과 두 번째 방법 사이의 혼란을 읽지 않는 것입니다.
McKay

아, 고마워. 그러나 그것은 여전히 ​​귀하의 게시물에 대한 두 번째 댓글에서 언급 한 문제를 남깁니다.
McKay

더 명확하게 말하면, "컴파일러, 첫 번째 방법과이 방법 사이에 완전히 혼란 스럽습니다. 방금 추가했습니다." 하지만 그렇지 않습니다. 다른 두 가지 방법으로 혼란 스럽습니다. params 메서드 및 일반 메서드.
McKay

@McKay : 음, state정확히 말하면 하나 또는 두 개가 아닌 3 가지 기능을 갖는 것이 혼란 스럽 습니다. 사실, 문제를 해결하기 위해 그들 하나 를 언급 하는 것으로 충분합니다 . 사용 가능한 함수 중 가장 일치하는 것은를 가진 함수 params이고, 두 번째는 generics매개 변수 set가있는 함수입니다. 세 번째 함수를 추가하면 함수에 혼란이 생깁니다 . 아마도 컴파일러가 생성하는 명확한 오류 메시지가 아닐 가능성이 큽니다.
Tigran 2011
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.