web-dev-qa-db-ja.com

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

_// 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クラスを書き換えるにはどうすればよいですか?

PDATE: 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.");
    }
}
_
106
AZ.

これは悪いプログラミング手法であり、C#では許可されていません。それは悪いプログラミング慣行です

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

  • あなたはそれが何をするのが好きで、それを再利用して拡張したいので、あなたはベースから派生しました。あなたがそれが何をするのが好きではなく、それを使って作業するのではなくそれを回避したい場合、なぜ最初にそれから派生したのですか?使用して拡張したい機能である場合は、自分でグランドベースから派生します。

  • ベースは、セキュリティまたはセマンティックの一貫性のために、ベースがグランドベースのメソッドをどのように使用するかの詳細によって維持される特定の不変式を必要とする場合があります。ベースの派生クラスがこれらの不変式を維持するコードをスキップできるようにすると、ベースが一貫性のない破損した状態になる可能性があります。

89
Eric Lippert

ここでこれを追加したいのです。なぜなら、多くの時間が経過しても人々はまだこの質問に戻っているからです。もちろんそれは悪い習慣ですが、著者が望むことを(原則として)行うことはまだ可能です:

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

C#からはできません。 ILから、これは実際にサポートされています。親クラスのいずれに対しても、virt以外の呼び出しを行うことができます...しないでください。 :)

23
rh.

答えは(あなたが探しているものではないことを知っています):

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

その後、派生クラスは親の状態を適切に制御できます。

10
JoshJordan

子クラスを特定の親クラスにキャストし、特定の実装を呼び出してみませんか?これは特別なケースの状況であり、特別なケースのソリューションを使用する必要があります。ただし、childrenメソッドでは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());
    }
}
6
Kruczkowski
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
Pavel

グランドベース関数を呼び出すために、第1レベルの派生クラスで単純な関数を作成することもできます

3
Rajesh

これに対する私の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アクセサーメソッドを追加する必要がある場合があります。

2
Julian Gold

派生クラスのソースにアクセスできないが、現在のメソッド以外に派生クラスのすべてのソースが必要な場合は、派生クラスを実行し、派生クラスの実装を呼び出すことをお勧めします。

以下に例を示します。

//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();  
     }         
}
1
yoel halb

祖父母クラスからメンバーメソッドを継承し、それを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"

単純に思えます......孫はここで祖父母メソッドを継承します。考えてみてください。それが、「オブジェクト」とToString()のようなそのメンバーがC#のすべてのクラスに継承される方法です。マイクロソフトは基本的な継承をうまく説明できていないと思います。ポリモーフィズムと実装に重点が置かれすぎています。私が彼らのドキュメントを掘り下げるとき、この非常に基本的なアイデアの例はありません。 :(

0
Stokely

以前の投稿からわかるように、クラスの機能を回避する必要がある場合、クラスアーキテクチャで何かが間違っていると考えることができます。それは本当かもしれませんが、大規模な成熟したプロジェクトでクラス構造を常に再構築またはリファクタリングできるとは限りません。さまざまなレベルの変更管理が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
Pierre