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

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

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



답변

아니요, 가상이 아닌 방법은 재정의 할 수 없습니다. 가장 가까운 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적절하게 전달합니다 .


답변

당신은 할 수 없습니다.

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

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

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


답변

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


답변

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

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

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


답변

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

    }
}


답변

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


답변

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

비가 상 메서드는 재정의 할 수 없습니다. 그러나 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());

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