가상이 아닌 방법을 재정의 할 수 있습니까?


92

비가 상 메서드를 재정의하는 방법이 있습니까? 또는 비슷한 결과를 제공하는 것 (원하는 메서드를 호출하는 새 메서드를 만드는 것 외에)?

Microsoft.Xna.Framework.Graphics.GraphicsDevice단위 테스트를 염두에두고 메서드를 재정의하고 싶습니다 .


8
과부하 또는 무시 를 의미 합니까? 오버로드 = 이름은 같지만 매개 변수가 다른 메서드를 추가합니다 (예 : Console.WriteLine의 다른 오버로드). Override = (대략) 메서드의 기본 동작을 변경합니다 (예 : Circle, Rectangle 등의 동작이 다른 Shape.Draw 메서드). 파생 클래스의 메서드를 항상 오버로드 할 수 있지만 재정의 는 가상 메서드에만 적용됩니다.
itowlson 2009

답변:


112

아니요, 가상이 아닌 방법은 재정의 할 수 없습니다. 가장 가까운 new방법은 동일한 이름 의 메서드를 만들어 메서드를 숨기는 것이지만 좋은 디자인 원칙을 위반하므로 권장되지 않습니다.

그러나 메서드를 숨겨도 진정한 가상 메서드 호출처럼 메서드 호출의 실행 시간 다형성 디스패치가 제공되지 않습니다. 이 예를 고려하십시오.

using System;

class Example
{
    static void Main()
    {
        Foo f = new Foo();
        f.M();

        Foo b = new Bar();
        b.M();
    }
}

class Foo
{
    public void M()
    {
        Console.WriteLine("Foo.M");
    }
}

class Bar : Foo
{
    public new void M()
    {
        Console.WriteLine("Bar.M");
    }
}

이 예제에서는 두 M메서드 모두 print 메서드를 호출합니다 Foo.M. 보시다시피이 접근 방식을 사용하면 해당 개체에 대한 참조가 올바른 파생 형식이지만 기본 메서드를 숨기면 다형성 중단 되는 한 메서드에 대한 새로운 구현을 가질 수 있습니다 .

이러한 방식으로 기본 메서드를 숨기지 않는 것이 좋습니다.

나는 C #의 기본 동작을 선호하는 사람들에게 메서드가 기본적으로 비가 상 (Java와 반대)이라는 편에서는 경향이 있습니다. 더 나아가서 클래스도 기본적으로 봉인되어야한다고 말하고 싶습니다. 상속은 적절하게 설계하기 어렵고 가상으로 표시되지 않은 메서드가 있다는 사실은 해당 메서드의 작성자가 메서드를 재정의 할 의도가 없음을 나타냅니다.

편집 : "실행 시간 다형성 디스패치" :

이것이 의미하는 바는 가상 메서드를 호출 할 때 실행 시간에 발생하는 기본 동작입니다. 예를 들어 이전 코드 예제에서 비가 상 메서드를 정의하는 대신 실제로 가상 메서드와 실제 재정의 된 메서드도 정의했다고 가정 해 보겠습니다.

이 경우 호출 b.Foo하면 CLR은 b참조가 가리키는 개체 유형을 올바르게 결정 Bar하고 호출을 M적절하게 전달합니다 .


6
"실행 시간 다형성 디스패치"가 기술적으로 올바른 표현이지만 거의 모든 사람의 머리를 넘어 설 것 같습니다!
Orion Edwards

3
작성자가 메서드 재정의를 허용하지 않으려는 의도가있는 것은 사실이지만 반드시 수행해야 할 올바른 일이라는 것은 사실이 아닙니다. 저는 XNA 팀이 IGraphicsDevice 인터페이스를 구현하여 사용자가 디버깅 및 단위 테스트에 더 많은 유연성을 제공해야한다고 생각합니다. 나는 매우 추악한 일을하도록 강요 받고 있으며 이것은 팀이 예견했을 것입니다. 추가 논의는 여기에서 찾을 수 있습니다 : forums.xna.com/forums/p/30645/226222.aspx
zfedoran 2009

2
@Orion 나도 그것을 이해하지 못했지만 빠른 구글 후에 "올바른"용어의 사용을보고 감사했습니다.
zfedoran 2009

10
나는 "저자가 의도하지 않았다"는 주장을 전혀 사지 않는다. 그것은 바퀴의 발명가가 바퀴가 자동차에 장착 될 것으로 예상하지 않았기 때문에 우리가 자동차에 사용할 수 없다고 말하는 것과 같습니다. 저는 휠 / 메소드가 어떻게 작동하는지 알고 있으며 약간 다른 작업을하고 싶습니다. 창작자가 원하든 원하지 않든 상관 없습니다. 죽은 스레드를 되살리려해서 죄송합니다. 이 문제를 해결하기 위해 정말 불편한 방법을 알아 내기 전에 약간의 증기를 뿜어 내야합니다 :)
Jeff

2
따라서 대규모 코드베이스를 상속하고 새로운 기능에 대한 테스트를 추가해야하지만이를 테스트하려면 많은 기계를 인스턴스화해야하는 경우는 어떨까요? Java에서는 해당 부분을 확장하고 스텁에 필요한 메서드를 재정의합니다. C #에서 메서드가 가상으로 표시되지 않으면 다른 메커니즘 (라이브러리 모킹 등)을 찾아야합니다.
Adam Parkin 2014 년

22

당신은 할 수 없습니다.

가상 메서드 만 재정의 할 수 있습니다 . 여기 에서 MSDN을 참조 하십시오 .

C #에서 파생 클래스는 기본 클래스 메서드와 이름이 같은 메서드를 포함 할 수 있습니다.

  • 기본 클래스 메서드는 가상으로 정의되어야합니다.

6

기본 클래스가 봉인되지 않은 경우이를 상속하여 기본 클래스를 숨기는 새 메서드를 작성할 수 있습니다 (메서드 선언에서 "new"키워드 사용). 그렇지 않으면 재정의하려는 원래 작성자가 아니기 때문에 재정의 할 수 없으므로 가상이 아닙니다.


3

오버로딩되고 혼란스러워지고 있다고 생각합니다. 오버로딩은 이름이 같지만 매개 변수 집합이 다른 두 개 이상의 메서드가 있음을 의미하고 재정의는 파생 클래스의 메서드에 대해 다른 구현이 있음을 의미합니다 (동작을 대체하거나 수정 함). 기본 클래스).

가상 메서드 인 경우 파생 된 클래스의 override 키워드를 사용하여 재정의 할 수 있습니다. 그러나 비가 상 메서드는 override 키워드 대신 new 키워드를 사용하여 기본 구현 만 숨길 수 있습니다. 컴파일러가 기본 메서드에 대한 정적 디스패치를 ​​사용하므로 호출자가 기본 형식으로 입력 된 변수를 통해 메서드에 액세스하는 경우 비가 상 경로는 쓸모가 없습니다 (즉, 파생 된 클래스의 코드가 호출되지 않음).

기존 클래스에 오버로드를 추가하는 것을 방해하는 것은 없지만 클래스에 대해 알고있는 코드 만 액세스 할 수 있습니다.


2

CLR을 해킹하지 않고 C # 클래스의 가상이 아닌 메서드를 재정의 할 수 없지만 클래스가 구현하는 모든 인터페이스 메서드를 재정의 할 수 있습니다. 밀봉되지 않은 것으로 간주

class GraphicsDevice: IGraphicsDevice {
    public void DoWork() {
        Console.WriteLine("GraphicsDevice.DoWork()");
    }
}

// with its interface
interface IGraphicsDevice {
    void DoWork();
}

// You can't just override DoWork in a child class,
// but if you replace usage of GraphicsDevice to IGraphicsDevice,
// then you can override this method (and, actually, the whole interface).

class MyDevice: GraphicsDevice, IGraphicsDevice {
    public new void DoWork() {
        Console.WriteLine("MyDevice.DoWork()");
        base.DoWork();
    }
}

그리고 여기에 데모가 있습니다.

class Program {
    static void Main(string[] args) {

        IGraphicsDevice real = new GraphicsDevice();
        var myObj = new MyDevice();

        // demo that interface override works
        GraphicsDevice myCastedToBase = myObj;
        IGraphicsDevice my = myCastedToBase;

        // obvious
        Console.WriteLine("Using real GraphicsDevice:");
        real.DoWork();

        // override
        Console.WriteLine("Using overriden GraphicsDevice:");
        my.DoWork();

    }
}


0

파생되지 않은 클래스에서 상속하는 경우 추상 슈퍼 클래스를 만들고 대신 다운 스트림에서 상속 할 수 있습니다.


0

비가 상 메서드를 재정의하는 방법이 있습니까? 또는 비슷한 결과를 제공하는 것 (원하는 메서드를 호출하는 새 메서드를 만드는 것 외에)?

비가 상 메서드는 재정의 할 수 없습니다. 그러나 modifier 키워드를 사용하여 유사한 결과를 얻을 있습니다 new.

class Class0
{
    public int Test()
    {
        return 0;
    }
}

class Class1 : Class0
{
    public new int Test()
    {
        return 1;
    }
}
. . .
// result of 1
Console.WriteLine(new Class1().Test());

또한 액세스 수정자가 동일한 지 확인해야합니다. 그렇지 않으면 상속을받지 못할 것입니다. 에서 다른 클래스를 상속하는 경우 의 키워드가 영향을 미치지 않습니다 액세스 수정이 동일하지 않는 객체, 그것으로부터 상속.Class1newClass1

액세스 수정자가 동일 하지 않은 경우 :

class Class0
{
    protected int Test()
    {
        return 0;
    }
}

class Class1 : Class0
{
    // different access modifier
    new int Test()
    {
        return 1;
    }
}

class Class2 : Class1
{
    public int Result()
    {
        return Test();
    }
}
. . .
// result of 0
Console.WriteLine(new Class2().Result());

... 액세스 수정이 경우 대 입니다 같은 :

class Class0
{
    protected int Test()
    {
        return 0;
    }
}

class Class1 : Class0
{
    // same access modifier
    protected new int Test()
    {
        return 1;
    }
}

class Class2 : Class1
{
    public int Result()
    {
        return Test();
    }
}
. . .
// result of 1
Console.WriteLine(new Class2().Result());

이전 답변에서 지적했듯이 이것은 좋은 설계 원칙이 아닙니다.


-5

추상 클래스와 추상 메서드를 사용하여이를 달성하는 방법이 있습니다.

중히 여기다

Class Base
{
     void MethodToBeTested()
     {
        ...
     }

     void Method1()
     {
     }

     void Method2()
     {
     }

     ...
}

이제 MethodToBeTested () 메서드의 다른 버전을 사용하려면 Class Base를 추상 클래스로 변경하고 MethodToBeTested () 메서드를 추상 메서드로 변경합니다.

abstract Class Base
{

     abstract void MethodToBeTested();

     void Method1()
     {
     }

     void Method2()
     {
     }

     ...
}

추상적 인 void MethodToBeTested ()를 사용하면 문제가 발생합니다. 구현이 사라졌습니다.

따라서 class DefaultBaseImplementation : Base기본 구현을 갖도록을 작성하십시오 .

그리고 class UnitTestImplementation : Base단위 테스트 구현을 위해 다른 것을 만듭니다 .

이 2 개의 새로운 클래스를 사용하여 기본 클래스 기능을 재정의 할 수 있습니다.

Class DefaultBaseImplementation : Base    
{
    override void MethodToBeTested()    
    {    
        //Base (default) implementation goes here    
    }

}

Class UnitTestImplementation : Base
{

    override void MethodToBeTested()    
    {    
        //Unit test implementation goes here    
    }

}

이제 구현 (재정의)하는 2 개의 클래스가 있습니다 MethodToBeTested().

필요에 따라 (파생 된) 클래스를 인스턴스화 할 수 있습니다 (예 : 기본 구현 또는 단위 테스트 구현 사용).


@slavoo : 안녕하세요 slavoo. 코드 업데이트에 감사드립니다. 그러나 이것을 반대하는 이유를 말씀해 주시겠습니까?
ShivanandSK 2015

6
질문에 대한 답이 아니기 때문입니다. 그는 가상으로 표시되지 않은 구성원을 재정의 할 수 있는지 묻고 있습니다. 추상으로 표시된 구성원을 구현해야한다는 것을 증명했습니다.
Lee Louviere
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.