이것은 스파르타입니까?


121

다음은 인터뷰 질문입니다. 해결책을 찾았지만 왜 작동하는지 잘 모르겠습니다.


질문:

Sparta클래스를 수정하지 않고 MakeItReturnFalsereturn 을 만드는 코드를 작성하십시오 false.

public class Sparta : Place
{
    public bool MakeItReturnFalse()
    {
        return this is Sparta;
    }
}

내 솔루션 : (스포일러)

public class Place
{
public interface Sparta { }
}

그러나 왜 대신 SpartaMakeItReturnFalse()참조 합니까?{namespace}.Place.Sparta{namespace}.Sparta


5
솔루션에 스포일러 경고 또는 무언가를 추가해 주시겠습니까? 나는 너무 실망해서 스스로 해결할 기회를 얻지 못합니다. 질문은 정말 놀랍습니다.
Karolis Kajenas

1
처음에는 스포일러 태그를 포함했지만이 게시물의 전체 기간 동안 커뮤니티에서 여러 번 편집했습니다. 미안합니다.
budi

1
이 게시물의 제목이 마음에 들지 않습니다. codegolf.SE에 더 적합합니다. 질문을 실제로 설명하는 것으로 변경할 수 있습니까?
Supuhstar

3
귀여운 퍼즐. 끔찍한 인터뷰 질문이지만 귀여운 퍼즐. 이제 이것이 작동하는 방법과 이유를 알았
으므로

답변:


117

그러나 왜 대신 SpartaMakeItReturnFalse()참조 합니까?{namespace}.Place.Sparta{namespace}.Sparta

기본적으로 이름 조회 규칙이 그렇게 말하고 있기 때문입니다. C # 5 사양에서 관련 명명 규칙은 섹션 3.8 ( "네임 스페이스 및 형식 이름")에 있습니다.

처음 몇 개의 글 머리 기호-잘리고 주석이 달린-다음과 같이 읽습니다.

  • namespace-or-type-name이 형식 I또는 형식 인 경우 I<A1, ..., AK> [이 경우 K = 0] :
    • K가 0이고 namespace-or-type-name이 제네릭 메서드 선언 내에 표시되는 경우 [nope, no generic methods]
    • 그렇지 않고 namespace-or-type-name이 형식 선언 내에 나타나면 각 인스턴스 형식 T (§10.3.1)에 대해 해당 형식 선언의 인스턴스 형식에서 시작하여 각 포함 클래스의 인스턴스 형식으로 계속됩니다. 구조체 선언 (있는 경우) :
      • 경우 K0이고 선언의 T이름의 입력 매개 변수를 포함하고 I, 그 공간 또는 타입 이름은 입력 파라미터를 지칭한다.[아니]
      • 그렇지 않으면 namespace-or-type-name이 유형 선언의 본문 내에 나타나 T 거나 기본 유형에 이름 IK유형 매개 변수 가있는 중첩 된 액세스 가능 유형이 포함 된 경우 namespace-or-type-name 은이를 참조합니다. 지정된 형식 인수로 구성된 형식입니다. [빙고!]
  • 이전 단계가 실패한 경우 각 namespace에 대해 namespace N-or-type-name이 발생하는 네임 스페이스 로 시작하여 각 엔 클로징 네임 스페이스 (있는 경우)에서 계속하고 전역 네임 스페이스로 끝나는 다음 단계가 평가됩니다. 엔티티를 찾을 때까지 :
    • 경우 K0이고 I있는 네임 스페이스의 이름입니다 N, 다음 ... [예, 그 것이다 성공]

따라서 마지막 글 머리 기호 는 첫 번째 글 머리 기호가 아무것도 찾지 못하면 Sparta 클래스를 선택하는 것입니다 ...하지만 기본 클래스 Place가 인터페이스를 정의 할 때 우리가 고려 하기 전에Sparta 발견 됩니다.Sparta 클래스를 .

중첩 유형을 Place.Sparta인터페이스가 아닌 클래스로 만들면 여전히 컴파일되고 반환 false되지만 컴파일러 Sparta는의 인스턴스가 클래스 의 인스턴스가 될 수 없음을 알고 있기 때문에 경고를 발행합니다 Place.Sparta. 마찬가지로 Place.Sparta인터페이스 를 유지 하지만 Sparta클래스 sealed를 만들면 Sparta인스턴스가 인터페이스를 구현할 수 없기 때문에 경고가 표시 됩니다.


2
또 다른 임의 관찰 : 원래 사용 Sparta, 클래스를 this is Place반환 true. 그러나 추가 public interface Place { }받는 Sparta클래스 것이 원인 this is Place으로 돌아갑니다 false. 내 머리를 돌립니다.
budi

@budi : 맞습니다. 이전 글 머리 기호가 Place인터페이스로 사용 되기 때문 입니다.
Jon Skeet

22

이름을 값으로 해석 할 때 정의의 "가까움"은 모호성을 해결하는 데 사용됩니다. "가장 가까운"정의가 무엇이든 선택됩니다.

인터페이스 Sparta는 기본 클래스 내에서 정의됩니다. 클래스 Sparta는 포함하는 네임 스페이스에 정의됩니다. 기본 클래스 내에 정의 된 것은 동일한 네임 스페이스에 정의 된 것보다 "가까이"있습니다.


1
이름 조회가 않은 경우 그리고 상상 하지 이런 식으로 작동합니다. 그런 다음 누군가 동일한 이름의 최상위 클래스를 추가하면 내부 클래스를 포함하는 작업 코드가 손상됩니다.
dan04

1
@ dan04 :하지만 그 대신 누군가가 최상위 클래스와 같은 이름 의 중첩 된 클래스 를 추가하면 중첩 된 클래스를 포함 하지 않는 작업 코드 가 깨집니다 . 따라서 그것은 정확히 완전한 "승리"시나리오가 아닙니다.
Jon Skeet

1
@JonSkeet 이러한 중첩 된 클래스를 추가하는 것은 작업 코드가 영향을받는 합리적인 이유가있는 영역의 변경이라고 말하고 변경 사항을 감시합니다. 완전히 관련이없는 최상위 클래스를 추가하는 것은 훨씬 더 많이 제거됩니다.
Angew는 더 이상 SO

2
@JonSkeet 그래도 약간 다른 모습의 Brittle Base Class 문제가 아닙니까?
주앙 멘데스

1
@ JoãoMendes : 예, 거의 그렇습니다.
Jon Skeet

1

아름다운 질문! 매일 C #을 수행하지 않는 사람들을 위해 약간 더 긴 설명을 추가하고 싶습니다. 질문은 일반적으로 이름 확인 문제를 상기시키는 좋은 질문이기 때문입니다.

다음과 같이 약간 수정 된 원본 코드를 가져옵니다.

  • 원래 표현식에서와 같이 비교하는 대신 유형 이름을 인쇄 해 봅시다 (예 : return this is Sparta .
  • 인터페이스 Athena를 정의하겠습니다 .Place인터페이스 이름 확인을 설명하기 수퍼 클래스 .
  • 모든 것을 매우 명확하게 만들기 위해 클래스에 this바인딩 된 형식 이름을 인쇄 해 보겠습니다 Sparta.

코드는 다음과 같습니다.

public class Place {
    public interface Athena { }
}

public class Sparta : Place
{
    public void printTypeOfThis()
    {
        Console.WriteLine (this.GetType().Name);
    }

    public void printTypeOfSparta()
    {
        Console.WriteLine (typeof(Sparta));
    }

    public void printTypeOfAthena()
    {
        Console.WriteLine (typeof(Athena));
    }
}

이제 Sparta개체를 만들고 세 가지 메서드를 호출합니다.

public static void Main(string[] args)
    {
        Sparta s = new Sparta();
        s.printTypeOfThis();
        s.printTypeOfSparta();
        s.printTypeOfAthena();
    }
}

우리가 얻는 출력은 다음과 같습니다.

Sparta
Athena
Place+Athena

그러나 Place 클래스를 수정하고 Sparta 인터페이스를 정의하면 :

   public class Place {
        public interface Athena { }
        public interface Sparta { } 
    }

그러면 Sparta이름 조회 메커니즘에서 먼저 사용할 수있는 인터페이스 인 인터페이스 가 다음 과 같이 변경됩니다.

Sparta
Place+Sparta
Place+Athena

그래서 우리는 MakeItReturnFalse이름 확인에 의해 처음 발견되는 슈퍼 클래스에서 Sparta 인터페이스를 정의하는 것만으로 함수 정의 에서 유형 비교를 효과적으로 엉망으로 만들었습니다.

그러나 C #은 이름 확인에서 슈퍼 클래스에 정의 된 인터페이스의 우선 순위를 지정하는 이유는 무엇입니까? @JonSkeet 알고 있습니다! 그의 답변을 읽으면 C #의 이름 확인 프로토콜에 대한 세부 정보를 얻을 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.