새로운 것과 재정의의 차이


198

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

사례 1 : 기본 클래스

public void DoIt();

사례 1 : 상속 된 클래스

public new void DoIt();

사례 2 : 기본 클래스

public virtual void DoIt();

사례 2 : 상속 된 클래스

public override void DoIt();

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


답변:


267

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

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 블로그


5
This indicates for the compiler to use the last defined implementation of a method. 메소드의 마지막 정의 구현을 어떻게 찾을 수 있습니까?
AminM

5
구체적인 클래스에서 시작하여 관심있는 메소드를 구현했는지 확인하십시오. 그렇다면 완료된 것입니다. 그렇지 않은 경우 상속 계층 구조에서 한 단계 위로 이동하십시오. 즉, 수퍼 클래스에 관심있는 메소드가 있는지 확인하십시오. 관심있는 방법을 찾을 때까지 계속하십시오.
csoltenborn

2
또한 override기본 클래스가 메소드를로 정의 할 때 메소드 만 가능 합니다 virtual. 이 단어는 virtual내가이 메서드를 호출 할 때 실제로 런타임에 호출하고있는 무슨 방법을 구현 정말 사전에 알 수없는, 그래서 이봐, 그것은 사실상, 파생 구현으로 대체 될 수 있었다 "라는 기본 클래스입니다. 그래서의 virtual의미는 메소드의 자리 표시 자 이것은 표시되지 않은 메소드를 virtual대체 할 수 없음을 의미하지만 파생 클래스의 비가 상 메소드를 수정 자로 대체new 할 수 있으며 파생 레벨에서만 액세스 할 수 있습니다.
Erik Bongers

177

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

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

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

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


2
기본 메소드를 숨기는 메소드를 캐스팅하는 것이 왜 위험한가요? 아니면 일반적으로 주조가 위험하다는 것을 암시합니까?
Mark

3
@Mark-호출자가 구현을 인식하지 못해 실수로 오용 할 수 있습니다.
Jon B

부모 방법 을 사용 override하거나 사용 new하지 않을 수 있습니까 virtual?
Aaron Franke

16

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


7

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

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

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


3

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();
}

수신중인 경고 또는 오류를 게시 할 수 있습니까? 이 코드는 처음 게시했을 때 정상적으로 작동했습니다.
Matthew Whited

이것은 모두 당신의 입학 포인트 클래스 (프로그램)에 붙여 넣어야합니다. 이 사이트에서 더 나은 형식을 위해 제거되었습니다.
Matthew Whited

3

두 경우의 차이점은 사례 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입니다.


3
  • 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();
}

1

키워드 override가 파생 클래스에서 사용되는 경우 키워드 가 상위 메소드를 대체합니다.

키워드 new가 파생 클래스에서 사용되는 경우 상위 메소드에 의해 숨겨지는 메소드를 파생하십시오.


1

나는 같은 질문을했고 실제로 혼란 스럽습니다. 기본 클래스 유형의 객체와 파생 클래스의 값으로 만 작동 하는 재정의 키워드를 고려해야합니다 . 이 경우에만 override 및 new의 효과를 볼 수 있습니다. 따라서 class Aand BB상속 A되면 from 과 같이 객체를 인스턴스화합니다.

A a = new B();

이제 호출 메소드는 상태를 고려합니다. 재정의 : 메서드의 함수를 확장 한 다음 파생 클래스의 메서드를 사용하는 반면 new 는 컴파일러에게 파생 클래스의 메서드를 숨기고 기본 클래스의 메서드를 대신 사용하도록 지시합니다. 그 주제에 대한 좋은 시선은 다음과 같습니다.

https://msdn.microsoft.com/EN-US/library/ms173153%28v=VS.140,d=hv.2%29.aspx?f=255&MSPPError=-2147217396


1

아래 기사는 vb.net에 있지만 새로운 대 재정의에 대한 설명은 이해하기가 매우 쉽다고 생각합니다.

https://www.codeproject.com/articles/17477/the-dark-shadow-of-overrides

기사의 어느 시점에서 다음 문장이 있습니다.

일반적으로 Shadows는 형식과 관련된 함수가 호출 된 것으로 가정하고 재정의는 개체 구현이 실행되는 것으로 가정합니다.

이 질문에 대한 대답은 완벽하지만이 기사에서는 이러한 두 키워드의 차이점에 대한 더 나은 의미를 추가 할 수있는 좋은 예를 제공한다고 생각합니다.


1

그 중에서도 새로운 것이 가장 혼란 스럽다. 실험을 통해 새로운 키워드는 개발자에게 유형을 명시 적으로 정의하여 상속 클래스 구현을 기본 클래스 구현으로 대체 할 수있는 옵션을 제공하는 것과 같습니다. 다른 방향으로 생각하는 것과 같습니다.

아래 예에서 결과는 유형이 명시 적으로 BaseClass 테스트로 정의 될 때까지 "파생 결과"를 반환하고 "기본 결과"만 반환됩니다.

class Program
{
    static void Main(string[] args)
    {
        var test = new DerivedClass();
        var result = test.DoSomething();
    }
}

class BaseClass
{
    public virtual string DoSomething()
    {
        return "Base result";
    }
}

class DerivedClass : BaseClass
{
    public new string DoSomething()
    {
        return "Derived result";
    }
}

3
이의를 제기 할 경우 의견을 추가하십시오. 명중하고 너무 비겁하다.
usefulBee

0

이 테스트에서는 기능적 차이가 표시되지 않습니다.

BaseClass bc = new BaseClass();

bc.DoIt();

DerivedClass dc = new DerivedClass();

dc.ShowIt();

이 exmample에서 호출되는 Doit은 호출 될 것으로 예상되는 것입니다.

차이점을 확인하려면 다음을 수행하십시오.

BaseClass obj = new DerivedClass();

obj.DoIt();

테스트를 실행하면 사례 1 (정의한대로)에서 DoIt()in BaseClass이 호출되고 사례 2 (정의 된대로)에서 DoIt()in DerivedClass이 호출 되는지 확인할 수 있습니다 .


-1

새 키워드는 기본 클래스 DoIt () 메소드를 숨기므로 첫 번째 경우 파생 클래스 DoIt () 메소드를 호출합니다.

두 번째 경우 재정의 된 DoIt ()을 호출합니다.

  public class A
{
    public virtual void DoIt()
    {
        Console.WriteLine("A::DoIt()");
    }
}

public class B : A
{
    new public void DoIt()
    {
        Console.WriteLine("B::DoIt()");
    }
}

public class C : A
{
    public override void DoIt()
    {
        Console.WriteLine("C::DoIt()");
    }
}

이 클래스의 인스턴스를 만들어 보자

   A instanceA = new A();

    B instanceB = new B();
    C instanceC = new C();

    instanceA.DoIt(); //A::DoIt()
    instanceB.DoIt(); //B::DoIt()
    instanceC.DoIt(); //B::DoIt()

위에서 모든 것이 예상됩니다. instanceB와 instanceC를 instanceA로 설정하고 DoIt () 메소드를 호출하고 결과를 확인하십시오.

    instanceA = instanceB;
    instanceA.DoIt(); //A::DoIt() calls DoIt method in class A

    instanceA = instanceC;
    instanceA.DoIt();//C::DoIt() calls DoIt method in class C because it was overriden in class C

instanceC.DoIt (); 당신에게 C : 작은 동전 ()가 아닌 B : 작은 동전 () 줄 것이다
BYS2

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