base.base.method ()를 호출하는 방법?


127
// Cannot change source code
class Base
{
    public virtual void Say()
    {
        Console.WriteLine("Called from Base.");
    }
}

// Cannot change source code
class Derived : Base
{
    public override void Say()
    {
        Console.WriteLine("Called from Derived.");
        base.Say();
    }
}

class SpecialDerived : Derived
{
    public override void Say()
    {
        Console.WriteLine("Called from Special Derived.");
        base.Say();
    }
}

class Program
{
    static void Main(string[] args)
    {
        SpecialDerived sd = new SpecialDerived();
        sd.Say();
    }
}

결과는 다음과 같습니다.

Special Derived에서 호출되었습니다.
Derived에서 호출되었습니다. / * 이것은 예상되지 않습니다 * /
Base에서 호출되었습니다.

중산층 "Derived"의 메서드가 호출되지 않도록 SpecialDerived 클래스를 어떻게 다시 작성할 수 있습니까?

업데이트 : Base 대신 Derived에서 상속하려는 이유는 Derived 클래스에 다른 많은 구현이 포함되어 있기 때문입니다. base.base.method()여기서 할 수 없기 때문에 가장 좋은 방법은 다음을 수행하는 것입니까?

// 소스 코드를 변경할 수 없습니다.

class Derived : Base
{
    public override void Say()
    {
        CustomSay();

        base.Say();
    }

    protected virtual void CustomSay()
    {
        Console.WriteLine("Called from Derived.");
    }
}

class SpecialDerived : Derived
{
    /*
    public override void Say()
    {
        Console.WriteLine("Called from Special Derived.");
        base.Say();
    }
    */

    protected override void CustomSay()
    {
        Console.WriteLine("Called from Special Derived.");
    }
}

업데이트에 맞게 편집합니다.
JoshJordan 2010

답변:


106

사람들이 많은 시간이 지난 후에도 여전히이 질문에 답하기 때문에 여기에 추가하고 싶습니다. 물론 나쁜 습관이지만 원칙적으로 작성자가 원하는 것을 수행하는 것은 여전히 ​​가능합니다.

class SpecialDerived : Derived
{
    public override void Say()
    {
        Console.WriteLine("Called from Special Derived.");
        var ptr = typeof(Base).GetMethod("Say").MethodHandle.GetFunctionPointer();            
        var baseSay = (Action)Activator.CreateInstance(typeof(Action), this, ptr);
        baseSay();            
    }
}

46
저는이 답변을 정말 좋아합니다. 왜냐하면 그 질문은 그것이 추천인지 아닌지, 좋은 생각인지 아닌지 였기 때문입니다. 질문은 그것을 할 방법이 있는지, 그렇다면 그 방법이 무엇인지였습니다.
Shavais

3
확장 성이 부족한 프레임 워크 / lib를 처리해야 할 때 특히 유용합니다. 예를 들어, 나는 고도로 확장 가능한 NHibernate를위한 그러한 솔루션에 의지 할 필요가 없었습니다. 그러나 Asp.Net Identity, Entity Framework, Asp.Net Mvc를 처리하기 위해 정기적으로 이러한 해킹을 사용하여 누락 된 기능이나 내 요구에 적합하지 않은 하드 코딩 된 동작을 처리합니다.
프레데릭

2
감사합니다! 다른 사람들에게 질문에 대한 답변으로 지침 인용을 중단하도록 언급하고 싶습니다. 꾸짖지 않고 이유를 묻습니다. 모른다면 대답하지 마세요! 나는 또한 모든 사람들이 코드 폴리스를 폭파하도록 격려하고 싶습니다. 그래서 아마도 그들이 대답이없는 것을 게시하는 것을 막을 것입니다. 질문에 답한 후 지침을 인용하고 싶은 마음이 들면 계속해서 언급하십시오.
DanW

1
기본 함수의 반환 값이 필요하면 어떻게해야합니까?
Perkins

2
신경 쓰지 마세요. 에 캐스트 Func<stuff>대신Action
퍼킨스

92

이것은 나쁜 프로그래밍 관행이며 C #에서는 허용되지 않습니다. 그것은 나쁜 프로그래밍 관행입니다.

  • 그랜드베이스의 세부 사항은베이스의 구현 세부 사항입니다. 당신은 그들에게 의존해서는 안됩니다. 기본 클래스는 그랜드베이스 위에 추상화를 제공합니다. 이를 피하기 위해 우회를 구축하는 것이 아니라 추상화를 사용해야합니다.

  • 이전 요점의 특정 예를 설명하기 위해 : 허용되는 경우이 패턴은 취약한 기본 클래스 오류에 취약한 코드를 만드는 또 다른 방법입니다. C에서 파생 B되는 파생 항목을 가정합니다 A. 의 메서드를 호출하는 C데 사용 base.base되는 코드 입니다 A. 다음의 저자 B들은 수업 시간에 너무 많은 기어를 넣어 것을 실현 B하고, 더 좋은 방법은 중간 클래스 확인하는 것입니다 B2그에서 유래 A하고, B도출에서을 B2. 변경 후 코드 인 C은 에서 가 B2아니라에서 메서드를 호출합니다A 않기 때문C 의 구현 세부 사항 B, 즉 직접 기본 클래스가 다음과 같다고 가정A, 결코 변하지 않을 것입니다. C #의 많은 디자인 결정은 다양한 종류의 취약한 기본 오류의 가능성을 완화하는 것입니다. base.base불법화 하기로 한 결정 은 이러한 실패 패턴의 특정 특징을 완전히 방지합니다.

  • 당신은 당신이하는 것을 좋아하고 재사용하고 확장하기를 원하기 때문에 당신의 기지에서 파생되었습니다. 그것이하는 일이 마음에 들지 않고 함께 일하는 것보다 그것을 해결하고 싶다면, 왜 처음부터 그 일에서 파생 되었습니까? 그 기능이 사용하고 확장하려는 경우 그랜드베이스에서 직접 파생하십시오.

  • 기본은 기본이 그랜드베이스의 방법을 사용하는 방법에 대한 세부 사항에 의해 유지되는 보안 또는 의미 적 일관성 목적을 위해 특정 불변성을 요구할 수 있습니다. 기본의 파생 클래스가 이러한 불변성을 유지하는 코드를 건너 뛰도록 허용하면 기본이 일관성이없고 손상된 상태가 될 수 있습니다.


4
@Jadoon : 그런 다음 상속보다 구성을 선호합니다. 클래스는 Base 또는 GrandBase의 인스턴스를 사용할 수 있으며 그런 다음 클래스는 인스턴스에 대한 기능을 연기 할 수 있습니다.
Eric Lippert 2015

4
@BlackOverlord :이 주제에 대해 강하게 느낀다면이 7 년 된 질문에 대한 답을 작성해 보는 것은 어떨까요? 그렇게하면 우리 모두가이 주제에 대한 당신의 지혜로부터 혜택을받을 수 있으며, 그러면 당신은 StackOverflow에 두 가지 답변을 작성 하여 총 기여도를 두 배로 늘릴 수 있습니다. 그것은 윈윈입니다.
Eric Lippert

4
@Eric Lippert : 제가 제 답을 쓰지 않은 데는 두 가지 이유가 있습니다. 첫째, 어떻게해야할지 몰랐고,이 주제를 찾은 이유입니다. 둘째,이 페이지 아래에 Evk의 포괄적 인 답변이 있습니다.
BlackOverlord

3
@DanW : 나는 답을 알고있다. 그것은 내 대답의 첫 번째 문장입니다. 원하는 기능은 나쁜 프로그래밍 관행이기 때문에 C #에서 허용되지 않습니다 . C #에서 어떻게 수행합니까? 당신은하지 않습니다. 이 질문에 대한 답을 모를 수도 있다는 생각은 재미있는 것입니다.하지만 더 이상 언급하지 않고 통과하도록하겠습니다. 이제이 답변이 만족스럽지 않다고 생각되면 더 나은 작업이라고 생각하는 답변을 직접 작성해보십시오. 그렇게하면 우리 모두 여러분의 지혜와 경험을 통해 배우고 올해 게시 한 답변 수를 두 배로 늘릴 수 있습니다.
Eric Lippert

11
@EricLippert 기본 코드에 결함이 있고 타사 컨트롤처럼 재정의해야하는 경우 어떻게 되나요? 그리고 구현에는 그랜드베이스에 대한 호출이 포함됩니까? 실제 응용 프로그램의 경우 중요한 특성 일 수 있으며 핫픽스가 필요할 수 있으므로 타사 공급 업체를 기다리는 것은 선택 사항이 아닐 수 있습니다. 생산 환경의 현실과 나쁜 관행.
Shiv

22

C #에서는 할 수 없습니다. IL에서는 실제로 지원됩니다. 부모 클래스에 가상이 아닌 호출을 할 수 있습니다.하지만하지 마십시오. :)


11

대답은 (당신이 찾고있는 것이 아님을 알고 있습니다) :

class SpecialDerived : Base
{
    public override void Say()
    {
        Console.WriteLine("Called from Special Derived.");
        base.Say();
    }
}

사실, 당신은 상속받은 클래스와 직접적인 상호 작용 만 할 수 있습니다. 해당 클래스를 계층으로 생각하십시오. 파생 클래스에 원하는만큼 또는 부모의 기능을 제공하는 것입니다.

편집하다:

귀하의 편집은 작동하지만 다음과 같은 것을 사용할 것이라고 생각합니다.

class Derived : Base
{
    protected bool _useBaseSay = false;

    public override void Say()
    {
        if(this._useBaseSay)
            base.Say();
        else
            Console.WriteLine("Called from Derived");
    }
}

물론 실제 구현에서는 확장 성과 유지 관리를 위해 다음과 같은 작업을 수행 할 수 있습니다.

class Derived : Base
{
    protected enum Mode
    {
        Standard,
        BaseFunctionality,
        Verbose
        //etc
    }

    protected Mode Mode
    {
        get; set;
    }

    public override void Say()
    {
        if(this.Mode == Mode.BaseFunctionality)
            base.Say();
        else
            Console.WriteLine("Called from Derived");
    }
}

그러면 파생 클래스가 부모의 상태를 적절하게 제어 할 수 있습니다.


3
Derived호출 Base.Say할 수 있는 보호 된 함수를 작성하여 호출 할 수있는 이유는 SpecialDerived무엇입니까? 더 간단하지 않습니까?
nawfal 2014 년

7

단순히 자식 클래스를 특정 부모 클래스로 캐스팅하고 특정 구현을 호출하지 않는 이유는 무엇입니까? 이것은 특수한 상황이며 특수한 경우 솔루션을 사용해야합니다. new그래도 자식 메서드 에서는 키워드 를 사용해야합니다 .

public class SuperBase
{
    public string Speak() { return "Blah in SuperBase"; }
}

public class Base : SuperBase
{
    public new string Speak() { return "Blah in Base"; }
}

public class Child : Base
{
    public new string Speak() { return "Blah in Child"; }
}

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        Child childObj = new Child();

        Console.WriteLine(childObj.Speak());

        // casting the child to parent first and then calling Speak()
        Console.WriteLine((childObj as Base).Speak()); 

        Console.WriteLine((childObj as SuperBase).Speak());
    }
}

2
기본 또는 자식에 대해 알 수없는 엔진이 있고 해당 엔진에서 호출 할 때 말하기가 올바르게 작동해야하는 경우 말하기는 새 것이 아니라 재정의 여야합니다. 만약 아이가베이스 기능의 99 %를 필요로하는데, 원 스피커의 경우에는 슈퍼베이스의 기능이 필요합니다 ... 이것이 제가 얘기 할 OP를 이해하는 상황입니다.이 경우이 방법은 작동하지 않습니다. 이것은 드문 일이 아니며 C #의 동작을 유발 한 보안 문제는 일반적으로별로 중요하지 않습니다.
Shavais

컨트롤 및 이벤트 호출 체인의 경우 참고로, 메서드는 종종 보호되므로 이와 같이 액세스 할 수 없습니다.
Shiv

2
이것은 상속을 사용하지 않으며 각 Speak에 완전히 고유 한 이름을 부여 할 수 있습니다.
Nick Sotiros

그래 그것은 100 % 상속 아니지만 부모로부터 인터페이스를 사용하고
Kruczkowski

5
public class A
{
    public int i = 0;
    internal virtual void test()
    {
        Console.WriteLine("A test");
    }
}

public class B : A
{
    public new int i = 1;
    public new void test()
    {
        Console.WriteLine("B test");
    }
}

public class C : B
{
    public new int i = 2;
    public new void test()
    {
        Console.WriteLine("C test - ");
        (this as A).test(); 
    }
}

4

그랜드베이스 함수를 호출하기 위해 첫 번째 수준 파생 클래스에서 간단한 함수를 만들 수도 있습니다.


정확히 그렇습니다. 이것은 모든 사람이 걱정하는 전체 추상화 체계를 보존하고, 추상화 체계가 가치있는 것보다 때때로 더 문제가되는 방식을 강조합니다.
Shavais

3

이에 대한 내 2c는 툴킷 클래스에서 호출하는 데 필요한 기능을 구현하고 필요한 곳에서 호출하는 것입니다.

// Util.cs
static class Util 
{
    static void DoSomething( FooBase foo ) {}
}

// FooBase.cs
class FooBase
{
    virtual void Do() { Util.DoSomething( this ); }
}


// FooDerived.cs
class FooDerived : FooBase
{
    override void Do() { ... }
}

// FooDerived2.cs
class FooDerived2 : FooDerived
{
    override void Do() { Util.DoSomething( this ); }
}

이것은 접근 권한에 대한 약간의 생각이 필요하며 internal, 기능을 용이하게하기 위해 접근 자 메서드를 추가해야 할 수도 있습니다.


1

파생 클래스 소스에 액세스 할 수 없지만 현재 메서드 외에 파생 클래스의 모든 소스가 필요한 경우 파생 클래스를 수행하고 파생 클래스의 구현을 호출하는 것이 좋습니다.

다음은 그 예입니다.

//No access to the source of the following classes
public class Base
{
     public virtual void method1(){ Console.WriteLine("In Base");}
}
public class Derived : Base
{
     public override void method1(){ Console.WriteLine("In Derived");}
     public void method2(){ Console.WriteLine("Some important method in Derived");}
}

//Here should go your classes
//First do your own derived class
public class MyDerived : Base
{         
}

//Then derive from the derived class 
//and call the bass class implementation via your derived class
public class specialDerived : Derived
{
     public override void method1()
     { 
          MyDerived md = new MyDerived();
          //This is actually the base.base class implementation
          MyDerived.method1();  
     }         
}

0

이전 게시물에서 볼 수 있듯이 클래스 기능을 우회해야하는 경우 클래스 아키텍처에 문제가 있다고 주장 할 수 있습니다. 그것은 사실 일 수 있지만, 대규모의 성숙한 프로젝트에서 클래스 구조를 항상 재구성하거나 리팩토링 할 수는 없습니다. 다양한 수준의 변경 관리가 하나의 문제 일 수 있지만, 리팩토링 후에도 기존 기능을 동일하게 유지하는 것이 항상 간단한 작업은 아닙니다. 특히 시간 제약이 적용되는 경우 더욱 그렇습니다. 성숙한 프로젝트에서는 코드 재구성 후 다양한 회귀 테스트가 통과하지 못하도록하는 것은 상당한 작업이 될 수 있습니다. 종종 나타나는 모호한 "이상 함"이 있습니다. 상속 된 기능이 실행되지 않아야하는 경우 (또는 다른 작업을 수행해야하는 경우) 유사한 문제가 발생했습니다. 우리가 아래에서 따랐던 접근 방식, 제외해야 할 기본 코드를 별도의 가상 기능에 넣는 것이 었습니다. 그런 다음이 함수는 파생 클래스에서 재정의되고 기능이 제외되거나 변경 될 수 있습니다. 이 예제에서 "Text 2"는 파생 클래스에서 출력되지 않도록 할 수 있습니다.

public class Base
{
    public virtual void Foo()
    {
        Console.WriteLine("Hello from Base");
    }
}

public class Derived : Base
{
    public override void Foo()
    {
        base.Foo();
        Console.WriteLine("Text 1");
        WriteText2Func();
        Console.WriteLine("Text 3");
    }

    protected virtual void WriteText2Func()
    {  
        Console.WriteLine("Text 2");  
    }
}

public class Special : Derived
{
    public override void WriteText2Func()
    {
        //WriteText2Func will write nothing when 
        //method Foo is called from class Special.
        //Also it can be modified to do something else.
    }
}

0

Grandparent 클래스에서 멤버 메서드를 상속하고 두 번째 클래스에서 재정의 한 다음 Grandchild 클래스에서 메서드를 다시 호출하는 것과 관련하여 이러한 질문이 많이있는 것 같습니다. 조부모의 구성원을 손자에게 물려주지 않겠습니까?

class A
{
    private string mystring = "A";    
    public string Method1()
    {
        return mystring;
    }
}

class B : A
{
    // this inherits Method1() naturally
}

class C : B
{
    // this inherits Method1() naturally
}


string newstring = "";
A a = new A();
B b = new B();
C c = new C();
newstring = a.Method1();// returns "A"
newstring = b.Method1();// returns "A"
newstring = c.Method1();// returns "A"

간단 해 보입니다 .... 손자는 여기에서 조부모 방법을 상속받습니다. 생각해보십시오 .... 이것이 "Object"와 ToString ()과 같은 멤버가 C #의 모든 클래스에 상속되는 방식입니다. 나는 마이크로 소프트가 기본적인 상속을 잘 설명하지 못했다고 생각한다. 다형성과 구현에 너무 많은 초점이 있습니다. 문서를 자세히 살펴보면이 기본적인 아이디어에 대한 예가 없습니다. :(


-2

기본 클래스 데이터에 액세스하려면 "this"키워드를 사용하거나이 키워드를 클래스에 대한 참조로 사용해야합니다.

namespace thiskeyword
{
    class Program
    {
        static void Main(string[] args)
        {
            I i = new I();
            int res = i.m1();
            Console.WriteLine(res);
            Console.ReadLine();
        }
    }

    public class E
    {
        new public int x = 3;
    }

    public class F:E
    {
        new public int x = 5;
    }

    public class G:F
    {
        new public int x = 50;
    }

    public class H:G
    {
        new public int x = 20;
    }

    public class I:H
    {
        new public int x = 30;

        public int m1()
        {
           // (this as <classname >) will use for accessing data to base class

            int z = (this as I).x + base.x + (this as G).x + (this as F).x + (this as E).x; // base.x refer to H
            return z;
        }
    }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.