이 다형성 C # 코드가 수행하는 작업을 인쇄하는 이유는 무엇입니까?
나는 최근에 정렬의 퍼즐 도움에 대한 이해 다음 코드 조각을 받았다 Polymorphism
및 Inheritance
C #을 - OOP있다.
// No compiling!
public class A
{
public virtual string GetName()
{
return "A";
}
}
public class B:A
{
public override string GetName()
{
return "B";
}
}
public class C:B
{
public new string GetName()
{
return "C";
}
}
void Main()
{
A instance = new C();
Console.WriteLine(instance.GetName());
}
// No compiling!
이제 퍼즐을 제시 한 다른 개발자와 오랜 시간 동안 대화를 나눈 후 결과물이 무엇인지 알지만 당신을 위해 그것을 망치지는 않을 것입니다. 내가 정말로 가지고있는 유일한 문제는 어떻게 그 출력을 얻는 지, 코드가 어떻게 진행되는지, 무엇을 상속하는지 등입니다.
C
정의 된 클래스 인 것처럼 보이므로 반환 될 것이라고 생각했습니다 . 그런 다음 B
C가 상속하기 때문에 반환 될지 여부에 대해 머리를 훑어 보았습니다 B
. 그러나 B
또한 상속합니다 A
(혼란 스러웠던 부분입니다!).
질문:
다형성과 상속이 출력을 검색하고 결국 화면에 표시하는 데 어떻게 역할을하는지 설명 할 수 있습니까?
이것에 대해 생각하는 올바른 방법은 모든 클래스가 특정 수의 "슬롯"을 갖는 객체를 필요로한다고 상상하는 것입니다. 그 슬롯은 메소드로 채워져 있습니다. "실제로 어떤 메서드가 호출됩니까?"라는 질문 다음 두 가지를 파악해야합니다.
- 각 슬롯의 내용은 무엇입니까?
- 어떤 슬롯이 호출됩니까?
슬롯을 고려하여 시작하겠습니다. 두 개의 슬롯이 있습니다. A의 모든 인스턴스에는 GetNameSlotA라고하는 슬롯이 있어야합니다. C의 모든 인스턴스에는 GetNameSlotC라고하는 슬롯이 있어야합니다. 이것이 C의 선언에서 "new"가 의미하는 것입니다. 이것은 "I want a new slot"을 의미합니다. "새 슬롯을 원하지 않습니다. GetNameSlotA를 다시 사용하고 싶습니다"를 의미하는 B의 선언에 대한 "재정의"와 비교됩니다.
물론 C는 A에서 상속하므로 C도 슬롯 GetNameSlotA를 가져야합니다. 따라서 C 인스턴스에는 GetNameSlotA 및 GetNameSlotC라는 두 개의 슬롯이 있습니다. C가 아닌 A 또는 B의 인스턴스에는 하나의 슬롯 GetNameSlotA가 있습니다.
이제 새로운 C를 만들 때 두 슬롯에 무엇이 들어가나요? GetNameA, GetNameB 및 GetNameC라는 세 가지 메서드가 있습니다.
A의 선언은 "GetNameSlotA에 GetNameA를 넣습니다"라고 말합니다. A는 C의 수퍼 클래스이므로 A의 규칙이 C에 적용됩니다.
B의 선언은 "GetNameSlotA에 GetNameB를 넣습니다"라고 말합니다. B는 C의 수퍼 클래스이므로 B의 규칙은 C의 인스턴스에 적용됩니다. 이제 A와 B 사이에 충돌이 있습니다. B가 더 파생 된 유형이므로 이깁니다. B의 규칙이 A의 규칙을 재정의 합니다. 따라서 선언에서 "재정의"라는 단어가 사용됩니다.
C의 선언은 "GetNameSlotC에 GetNameC를 넣습니다"라고 말합니다.
따라서 새 C에는 두 개의 슬롯이 있습니다. GetNameSlotA에는 GetNameB가 포함되고 GetNameSlotC에는 GetNameC가 포함됩니다.
이제 어떤 슬롯에 어떤 방법이 있는지 확인 했으므로 첫 번째 질문에 답했습니다.
이제 두 번째 질문에 답해야합니다. 어떤 슬롯이 호출됩니까?
당신이 컴파일러 인 것처럼 생각 해보세요. 변수가 있습니다. 그것에 대해 아는 것은 유형 A라는 것뿐입니다. 해당 변수에 대한 메서드 호출을 해결하라는 메시지가 표시됩니다. A에서 사용 가능한 슬롯을 살펴보면 일치하는 슬롯은 GetNameSlotA뿐입니다. A 유형의 변수 만 있기 때문에 GetNameSlotC에 대해 알지 못합니다. C에만 적용되는 슬롯을 찾는 이유는 무엇입니까?
따라서 이것은 GetNameSlotA에있는 모든 것에 대한 호출입니다. 우리는 이미 런타임에 GetNameB가 해당 슬롯에있을 것이라고 결정했습니다. 따라서 이것은 GetNameB에 대한 호출입니다.
여기서 중요한 점 은 C # 오버로드 해결에서 슬롯을 선택하고 해당 슬롯 에있는 모든 것에 대한 호출을 생성한다는 것입니다.
It should return "B" because B.GetName()
is held in the little virtual table box for the A.GetName()
function. C.GetName()
is a compile time "override", it doesn't override the virtual table so you can't retrieve it through a pointer to A
.
Easy, you only have to keep the inheritance tree in mind.
In your code, you hold a reference to a class of type 'A', which is instantiated by an instance of type 'C'. Now, to resolve the exact method address for the virtual 'GetName()' method, the compiler goes up the inheritance hierarchy and looks for the most recent override (note that only 'virtual' is an override, 'new' is something completely different...).
That's in short what happens. The new keyword from type 'C' would only play a role if you would call it on an instance of type 'C' and the compiler then would negate all possible inheritance relations altogether. Strictly spoken, this has nothing to do at all with polymorphism - you can see that from the fact that whether you mask a virtual or non-virtual method with the 'new' keyword doesn't make any difference...
'New' in class 'C' means exactly that: If you call 'GetName()' on an instance of this (exact) type, then forget everything and use THIS method. 'Virtual' in contrary means: Go up the inheritance tree until you find a method with this name, no matter what the exact type of the calling instance is.
OK, the post is a bit old, but it's an excellent question and an excellent answer, so I just wanted to add my thoughts.
Consider the following example, which is the same as before, except for the main function:
// No compiling!
public class A
{
public virtual string GetName()
{
return "A";
}
}
public class B:A
{
public override string GetName()
{
return "B";
}
}
public class C:B
{
public new string GetName()
{
return "C";
}
}
void Main()
{
Console.Write ( "Type a or c: " );
string input = Console.ReadLine();
A instance = null;
if ( input == "a" ) instance = new A();
else if ( input == "c" ) instance = new C();
Console.WriteLine( instance.GetName() );
}
// No compiling!
Now it's really obvious that the function call cannot be bound to a specific function at compile time. Something must be compiled however, and that information can only depend on the type of the reference. So, it would be impossible to execute the GetName function of class C with any reference other than one of type C.
P.S. Maybe I should've used the term method in stead of function, but as Shakespeare said: A function by any other name is still a function :)
Actually, I think it should display C, because new operator just hides all ancestor methods with the same name. So, with methods of A and B hidden, only C remains visible.
http://msdn.microsoft.com/en-us/library/51y09td4%28VS.71%29.aspx#vclrfnew_newmodifier
'programing tip' 카테고리의 다른 글
GIL 때문에 다중 스레드 Python 코드에서 잠금이 필요하지 않습니까? (0) | 2020.11.08 |
---|---|
Firebug를 감지하는 Javascript? (0) | 2020.11.08 |
Redis 키에서 콜론의 목적은 무엇입니까 (0) | 2020.11.08 |
JPA 기준 자습서 (0) | 2020.11.08 |
Django-POST 요청에서 값 가져 오기 (0) | 2020.11.08 |