다음은 인터뷰 질문입니다. 해결책을 찾았지만 왜 작동하는지 잘 모르겠습니다.
질문:
Sparta
클래스를 수정하지 않고 MakeItReturnFalse
return 을 만드는 코드를 작성하십시오 false
.
public class Sparta : Place
{
public bool MakeItReturnFalse()
{
return this is Sparta;
}
}
내 솔루션 : (스포일러)
public class Place
{
public interface Sparta { }
}
그러나 왜 대신 Sparta
에 MakeItReturnFalse()
참조 합니까?{namespace}.Place.Sparta
{namespace}.Sparta
답변
그러나 왜 대신
Sparta
에MakeItReturnFalse()
참조 합니까?{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)에 대해 해당 형식 선언의 인스턴스 형식에서 시작하여 각 포함 클래스의 인스턴스 형식으로 계속됩니다. 구조체 선언 (있는 경우) :
- 경우
K
0이고 선언의T
이름의 입력 매개 변수를 포함하고I
, 그 공간 또는 타입 이름은 입력 파라미터를 지칭한다.[아니]- 그렇지 않으면 namespace-or-type-name이 유형 선언의 본문 내에 나타나
T
거나 기본 유형에 이름I
및K
유형 매개 변수 가있는 중첩 된 액세스 가능 유형이 포함 된 경우 namespace-or-type-name 은이를 참조합니다. 지정된 형식 인수로 구성된 형식입니다. [빙고!]- 이전 단계가 실패한 경우 각 namespace에 대해 namespace
N
-or-type-name이 발생하는 네임 스페이스 로 시작하여 각 엔 클로징 네임 스페이스 (있는 경우)에서 계속하고 전역 네임 스페이스로 끝나는 다음 단계가 평가됩니다. 엔티티를 찾을 때까지 :
- 경우
K
0이고I
있는 네임 스페이스의 이름입니다N
, 다음 … [예, 그 것이다 성공]
따라서 마지막 글 머리 기호 는 첫 번째 글 머리 기호가 아무것도 찾지 못하면 Sparta
클래스를 선택하는 것입니다 …하지만 기본 클래스 Place
가 인터페이스를 정의 할 때 우리가 고려 하기 전에Sparta
발견 됩니다.Sparta
클래스를 .
중첩 유형을 Place.Sparta
인터페이스가 아닌 클래스로 만들면 여전히 컴파일되고 반환 false
되지만 컴파일러 Sparta
는의 인스턴스가 클래스 의 인스턴스가 될 수 없음을 알고 있기 때문에 경고를 발행합니다 Place.Sparta
. 마찬가지로 Place.Sparta
인터페이스 를 유지 하지만 Sparta
클래스 sealed
를 만들면 Sparta
인스턴스가 인터페이스를 구현할 수 없기 때문에 경고가 표시 됩니다.
답변
이름을 값으로 해석 할 때 정의의 “가까움”은 모호성을 해결하는 데 사용됩니다. “가장 가까운”정의가 무엇이든 선택됩니다.
인터페이스 Sparta
는 기본 클래스 내에서 정의됩니다. 클래스 Sparta
는 포함하는 네임 스페이스에 정의됩니다. 기본 클래스 내에 정의 된 것은 동일한 네임 스페이스에 정의 된 것보다 “가까이”있습니다.
답변
아름다운 질문! 매일 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 #의 이름 확인 프로토콜에 대한 세부 정보를 얻을 수 있습니다.