비가 상 메서드를 재정의하는 방법이 있습니까? 또는 비슷한 결과를 제공하는 것 (원하는 메서드를 호출하는 새 메서드를 만드는 것 외에)?
Microsoft.Xna.Framework.Graphics.GraphicsDevice
단위 테스트를 염두에두고 메서드를 재정의하고 싶습니다 .
비가 상 메서드를 재정의하는 방법이 있습니까? 또는 비슷한 결과를 제공하는 것 (원하는 메서드를 호출하는 새 메서드를 만드는 것 외에)?
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
적절하게 전달합니다 .
오버로딩되고 혼란스러워지고 있다고 생각합니다. 오버로딩은 이름이 같지만 매개 변수 집합이 다른 두 개 이상의 메서드가 있음을 의미하고 재정의는 파생 클래스의 메서드에 대해 다른 구현이 있음을 의미합니다 (동작을 대체하거나 수정 함). 기본 클래스).
가상 메서드 인 경우 파생 된 클래스의 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());
또한 액세스 수정자가 동일한 지 확인해야합니다. 그렇지 않으면 상속을받지 못할 것입니다. 에서 다른 클래스를 상속하는 경우 의 키워드가 영향을 미치지 않습니다 액세스 수정이 동일하지 않는 객체, 그것으로부터 상속.Class1
new
Class1
액세스 수정자가 동일 하지 않은 경우 :
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());
이전 답변에서 지적했듯이 이것은 좋은 설계 원칙이 아닙니다.
추상 클래스와 추상 메서드를 사용하여이를 달성하는 방법이 있습니다.
중히 여기다
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()
.
필요에 따라 (파생 된) 클래스를 인스턴스화 할 수 있습니다 (예 : 기본 구현 또는 단위 테스트 구현 사용).