base.base.method()を呼び出す方法は?


126
// 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から呼び出されました。/ *これは予期されない* /
ベースから呼び出されました。

中間クラスの「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
拡張性に欠けるフレームワーク/ライブラリを扱う必要がある場合に特に役立ちます。たとえば、拡張性の高いNHibernateのソリューションに頼る必要はありませんでした。しかし、Asp.Net Identity、Entity Framework、Asp.Net Mvcを扱うために、欠けている機能や、自分のニーズに適さないハードコードされた動作を処理するために、このようなハックを定期的に使用することになります。
フレデリック

2
それをありがとう!質問に応じてガイドラインを引用するのをやめるように他の人々に言及したいと思います。叱られるのではなく、理由を尋ねられました。わからない場合は、答えないでください。私はまた、誰もがコードポリスを爆破するよう奨励したいと思います。そうすれば、彼らが非回答を投稿するのを思いとどまらせるでしょう。質問に回答した後、ガイドラインを引用せざるを得ない場合は、先に進んでそれについて言及してください。
DanW 2017

1
基になる関数からの戻り値が必要な場合はどうなりますか?
パーキンス

2
気にしないで、それを考え出した。Func<stuff>代わりにキャストAction
Perkins

92

これは不適切なプログラミング手法であり、C#では許可されていません。なぜなら、それは悪いプログラミング習慣です。

  • グランドベースの詳細は、ベースの実装の詳細です。あなたはそれらに頼るべきではありません。基本クラスは、グランドベースの上に抽象化を提供します。あなたはそれを避けるためにバイパスを構築するのではなく、その抽象化を使用するべきです。

  • 前のポイントの特定の例を説明すると、このパターンは、もし許可されれば、コードを脆弱な基本クラスの障害の影響を受けやすくするもう1つの方法になります。が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に2つの回答を書いて、合計貢献量を2倍にすることになります。それは双方に有利です。
Eric Lippert、2017年

4
@Eric Lippert:私が自分の答えを書かなかった理由は2つあります:最初に、私はそれをする方法を知らなかった、それが私がこのトピックを見つけた理由です。第二に、このページの下のEvkによる包括的な答えがあります。
BlackOverlord 2017年

3
@DanW:私は答えを知っています。それは私の答えの最初の文です:望ましい機能はプログラミングの習慣が悪いため、C#では許可されていません。これをC#でどのように行いますか?あなたはしません。この質問への答えがわからないかもしれないという考えは面白いですが、それ以上のコメントはせずに通過させます。さて、この答えが満足のいくものではない場合は、より良い仕事をすると思う自分の答えを書いてみませんか?そうすれば、私たち全員があなたの知恵と経験から学び、今年投稿した回答の数も2倍になります。
Eric Lippert、2017

11
@EricLippertベースコードに欠陥があり、サードパーティのコントロールのようにオーバーライドする必要がある場合はどうなりますか?そして、実装にはグランドベースへの呼び出しが含まれていますか?実世界のアプリケーションの場合、それは重要な性質を持つ可能性があり、修正プログラムが必要になる可能性があるため、サードパーティベンダーを待つことは選択肢にならない場合があります。生産環境の悪い習慣と現実。
Shiv 2018年

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%を必要とするが、1つの話す場合には、スーパーベースの機能が必要です...それは、私がOPが話していることを理解しているような状況です。その場合、この方法は機能しません。これは珍しいことではなく、C#の動作を引き起こしたセキュリティ上の懸念は、一般的にそれほど問題ではありません。
Shavais

コントロールとイベントコールチェーンの場合の注意点として、メソッドは保護されていることが多く、このようにアクセスできません。
Shiv 2018年

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

3

最初のレベルの派生クラスで単純な関数を作成して、グランドベース関数を呼び出すこともできます。


まさにそうであり、これは誰もがとても心配している抽象化スキーム全体を保持し、抽象化スキームが時々彼らの価値よりも厄介である方法を強調します。
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

以前の投稿からわかるように、クラスの機能を回避する必要がある場合、クラスアーキテクチャに問題があると主張できます。それは本当かもしれませんが、大規模な成熟したプロジェクトでクラス構造を常に再構築またはリファクタリングできるとは限りません。さまざまなレベルの変更管理が1つの問題になる可能性がありますが、特に時間の制約が適用される場合は、リファクタリング後に既存の機能を同じように動作させることは、常に簡単な作業ではありません。成熟したプロジェクトでは、コードの再構築後にさまざまなリグレッションテストに合格しないようにするのは非常に困難です。多くの場合、不明瞭な「奇妙さ」が現れます。継承された機能が実行されない(または他の機能を実行する)必要がある場合に、同様の問題が発生しました。以下のアプローチは、除外する必要がある基本コードを別の仮想関数に配置することでした。次に、この関数を派生クラスでオーバーライドし、機能を除外または変更できます。この例では、「テキスト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

祖父母クラスからメンバーメソッドを継承し、2番目のクラスでそれをオーバーライドして、孫クラスからそのメソッドを再度呼び出すことに関して、これらの質問がたくさんあるようです。祖父母のメンバーを孫に継承するだけではどうですか?

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#のすべてのクラスに継承される方法です。Microsoftは基本的な継承をうまく説明できていないと思います。ポリモーフィズムと実装にあまりにも重点が置かれています。私が彼らのドキュメントを掘り下げたとき、この非常に基本的なアイデアの例はありません。:(


-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.