[C#] 새로운 것과 재정의의 차이

다음의 차이점이 무엇인지 궁금합니다.

사례 1 : 기본 클래스

public void DoIt();

사례 1 : 상속 된 클래스

public new void DoIt();

사례 2 : 기본 클래스

public virtual void DoIt();

사례 2 : 상속 된 클래스

public override void DoIt();

사례 1과 2는 모두 내가 실행 한 테스트를 기반으로 동일한 효과를 나타냅니다. 차이점이나 선호하는 방법이 있습니까?



답변

재정의 수정자는 가상 메서드에 사용될 수 있으며 추상 메서드에 사용해야합니다. 이것은 컴파일러가 마지막으로 정의 된 메소드 구현을 사용함을 나타냅니다. 기본 클래스에 대한 참조에서 메소드가 호출 되더라도이를 대체하는 구현을 사용합니다.

public class Base
{
    public virtual void DoIt()
    {
    }
}

public class Derived : Base
{
    public override void DoIt()
    {
    }
}

Base b = new Derived();
b.DoIt();                      // Calls Derived.DoIt

Derived.DoIt재정의 하면 호출 합니다 Base.DoIt.

새로운 수정자는 부모 클래스 구현 대신 자식 클래스 구현을 사용하도록 컴파일러에 지시합니다. 클래스를 참조하지 않지만 부모 클래스를 참조하는 모든 코드는 부모 클래스 구현을 사용합니다.

public class Base
{
    public virtual void DoIt()
    {
    }
}

public class Derived : Base
{
    public new void DoIt()
    {
    }
}

Base b = new Derived();
Derived d = new Derived();

b.DoIt();                      // Calls Base.DoIt
d.DoIt();                      // Calls Derived.DoIt

첫 번째 호출 Base.DoItDerived.DoIt. 그것들은 사실상 기본 메소드를 재정의하는 파생 메소드가 아니라 동일한 이름을 갖는 완전히 별개의 두 메소드입니다.

출처 : Microsoft 블로그


답변

virtual : 메소드가 상속자에 의해 대체 될 수 있음을 나타냅니다.

override : 기본 클래스에서 가상 메소드의 기능을 대체하여 다른 기능을 제공합니다.

new : 원래의 메소드를 숨기고 (가상 일 필요는 없음) 다른 기능을 제공합니다. 반드시 필요한 곳에만 사용해야합니다.

메소드를 숨길 때 기본 클래스로 캐스트하여 원래 메소드에 계속 액세스 할 수 있습니다. 일부 시나리오에서는 유용하지만 위험합니다.


답변

첫 번째 경우 부모 클래스에서 정의를 숨기고 있습니다. 즉, 객체를 자식 클래스로 처리 할 때만 호출됩니다. 클래스를 부모 유형으로 캐스트하면 부모의 메소드가 호출됩니다. 두 번째 경우, 메소드가 대체되고 오브젝트가 하위 클래스 또는 상위 클래스로 캐스트되는지에 관계없이 호출됩니다.


답변

다음을 시도하십시오 : (case1)

((BaseClass)(new InheritedClass())).DoIt()

편집 : virtual + 재정의는 런타임에 해결되므로 재정의는 실제로 가상 메서드를 재정의하지만 new는 동일한 이름으로 새 메서드를 만들고 이전을 숨기고 컴파일 타임에 해결됩니다.> 컴파일러는 ‘ 보고있다


답변

1을 사용하면 유형이 기본 클래스로 선언 된 동안 상속 된 클래스의 DoIt () 메소드를 호출하면 기본 클래스의 동작도 볼 수 있습니다.

/* Results
Class1
Base1
Class2
Class2
*/
public abstract class Base1
{
    public void DoIt() { Console.WriteLine("Base1"); }
}
public  class Class1 : Base1
{
    public new void DoIt() { Console.WriteLine("Class1"); }
}
public abstract class Base2
{
    public virtual void DoIt() { Console.WriteLine("Base2"); }
}
public class Class2 : Base2
{
    public override void DoIt() { Console.WriteLine("Class2"); }
}
static void Main(string[] args)
{
    var c1 = new Class1();
    c1.DoIt();
    ((Base1)c1).DoIt();

    var c2 = new Class2();
    c2.DoIt();
    ((Base2)c2).DoIt();
    Console.Read();
}


답변

두 경우의 차이점은 사례 1에서 기본 DoIt방법이 무시되지 않고 숨겨져 있다는 것입니다. 이것이 의미하는 것은 변수의 유형에 따라 어떤 메소드가 호출되는지에 달려 있다는 것입니다. 예를 들면 다음과 같습니다.

BaseClass instance1 = new SubClass();
instance1.DoIt(); // Calls base class DoIt method

SubClass instance2 = new SubClass();
instance2.DoIt(); // Calls sub class DoIt method

이것은 실제로 혼란스럽고 예상치 못한 동작을 초래할 수 있으므로 가능하면 피해야합니다. 따라서 선호되는 방법은 사례 2입니다.


답변

  • new는 REFERENCE 유형 (의 왼쪽)을 존중하여 =참조 유형의 메소드를 실행 함을 의미합니다 . 재정의 된 메소드에 new키워드 가 없으면 그대로 동작합니다. 또한 비다 형성 상속 이라고도 합니다. 즉,“기본 클래스에서 같은 이름의 메서드와는 전혀 관련이없는 파생 클래스에서 완전히 새로운 메서드를 만들고 있습니다.” -휘태커는
  • overridevirtual기본 클래스에서 키워드 와 함께 사용해야하는 , OBJECT 유형 (의 오른쪽)을 존중 =하므로 참조 유형에 관계없이 메소드를 재정의 함을 의미합니다. 또한 다형성 상속 이라고도 합니다.

두 키워드가 서로 반대라는 것을 명심하는 나의 길.

override: virtual메소드를 대체하려면 키워드를 정의해야합니다. override기본 클래스로 인스턴스화되는 경우 참조 유형 (기본 클래스 또는 파생 클래스의 참조)에 관계없이 키워드를 사용 하는 메소드는 기본 클래스의 메소드를 실행합니다. 그렇지 않으면 파생 클래스의 메서드가 실행됩니다.

new: 키워드와 달리 메소드에서 키워드를 사용하는 override경우 참조 유형이 중요합니다. 파생 클래스로 인스턴스화되고 참조 유형이 기본 클래스 인 경우 기본 클래스의 메소드가 실행됩니다. 파생 클래스로 인스턴스화되고 참조 유형이 파생 클래스 인 경우 파생 클래스의 메소드가 실행됩니다. 즉, override키워드의 대비입니다 . Passant는 메소드에 새 키워드를 추가하거나 잊어 버린 경우 new키워드가 사용될 때 컴파일러가 기본적으로 작동합니다 .

class A
{
    public string Foo()
    {
        return "A";
    }

    public virtual string Test()
    {
        return "base test";
    }
}

class B: A
{
    public new string Foo()
    {
        return "B";
    }
}

class C: B
{
    public string Foo()
    {
        return "C";
    }

    public override string Test() {
        return "derived test";
    }
}

메인 전화 :

A AClass = new B();
Console.WriteLine(AClass.Foo());
B BClass = new B();
Console.WriteLine(BClass.Foo());
B BClassWithC = new C();
Console.WriteLine(BClassWithC.Foo());

Console.WriteLine(AClass.Test());
Console.WriteLine(BClassWithC.Test());

산출:

A
B
B
base test
derived test

새로운 코드 예제

하나씩 댓글을 달아 코드를 가지고 놀아보세요.

class X
{
    protected internal /*virtual*/ void Method()
    {
        WriteLine("X");
    }
}
class Y : X
{
    protected internal /*override*/ void Method()
    {
        base.Method();
        WriteLine("Y");
    }
}
class Z : Y
{
    protected internal /*override*/ void Method()
    {
        base.Method();
        WriteLine("Z");
    }
}

class Programxyz
{
    private static void Main(string[] args)
    {
        X v = new Z();
        //Y v = new Z();
        //Z v = new Z();
        v.Method();
}