Как позвонить 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 вместо Base, заключается в том, что класс 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.");
    }
}

13 ответов

Просто хочу добавить это сюда, так как люди все еще возвращаются к этому вопросу даже спустя много времени. Конечно, это плохая практика, но все же возможно (в принципе) делать то, что хочет автор:

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

Это плохая практика программирования и не допускается в C#. Это плохая практика программирования, потому что

  • Детали grandbase являются деталями реализации базы; Вы не должны полагаться на них. Базовый класс обеспечивает абстракцию поверх грандбазы; Вы должны использовать эту абстракцию, а не строить обход, чтобы избежать этого.

  • Вы получили свою базу, потому что вам нравится то, что она делает, и вы хотите использовать ее и расширять. Если вам не нравится то, что он делает, и вы хотите обойти это, а не работать с ним, то почему вы в первую очередь извлекли из этого пользу? Получите из базы данных сами, если это функциональность, которую вы хотите использовать и расширять.

  • База может потребовать определенных инвариантов для целей безопасности или семантической согласованности, которые поддерживаются деталями того, как база использует методы грандбазы. Разрешение производному классу базы пропускать код, который поддерживает эти инварианты, может привести базу в несовместимое, поврежденное состояние.

Вы не можете из C#. С IL это фактически поддерживается. Вы можете сделать вызов не в виртуальной среде для любого из ваших родительских классов... но, пожалуйста, не делайте этого.:)

Ответ (который я знаю не то, что вы ищете):

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

Затем производные классы могут соответствующим образом контролировать состояние своих родителей.

Почему бы просто не привести дочерний класс к определенному родительскому классу и затем вызвать конкретную реализацию? Это особая ситуация, и следует использовать решение для особого случая. Вам придется использовать 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());
    }
}
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(); 
    }
}

Вы также можете сделать простую функцию в производном классе первого уровня, чтобы вызвать основную базовую функцию

Мой 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 методы доступа для облегчения работы.

В тех случаях, когда у вас нет доступа к источнику производного класса, но вам нужен весь источник производного класса, кроме текущего метода, я бы порекомендовал вам также создать производный класс и вызвать реализацию производного класса.

Вот пример:

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

Как видно из предыдущих постов, можно утверждать, что если необходимо обойти функциональность класса, то что-то не так в архитектуре класса. Это может быть правдой, но не всегда можно реструктурировать или реорганизовать структуру классов в большом зрелом проекте. Различные уровни управления изменениями могут быть одной из проблем, но поддерживать работоспособность существующих функций после рефакторинга не всегда тривиальная задача, особенно если применяются временные ограничения. В зрелом проекте может оказаться довольно трудным препятствовать прохождению различных регрессионных тестов после реструктуризации кода; часто появляются неясные "странности". У нас была похожая проблема, в некоторых случаях унаследованные функции не должны выполняться (или должны выполнять что-то еще). Подход, который мы использовали ниже, заключался в том, чтобы поместить основной код, который необходимо исключить, в отдельную виртуальную функцию. Затем эта функция может быть переопределена в производном классе, а функциональность исключена или изменена. В этом примере "Текст 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.
    }
}

Кажется, есть много таких вопросов, связанных с наследованием метода-члена от класса 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"

Кажется простым.... внук наследует здесь метод бабушек и дедушек. Подумайте об этом..... вот как "Объект" и его члены, такие как ToString(), наследуются для всех классов в C#. Я думаю, что Microsoft не проделала хорошую работу по объяснению основного наследования. Слишком много внимания уделяется полиморфизму и реализации. Когда я копаюсь в их документации, нет примеров этой самой основной идеи.:(

У меня была та же проблема, что и в OP, где я хотел переопределить только один метод в среднем классе, оставив все остальные методы в покое. Мой сценарий был:

Класс А - базовый класс, доступ к БД, нередактируемый.

Класс B : A - специфические функции "типа записи" (редактируемые, но только в случае обратной совместимости).

Класс C : B - одно конкретное поле для одного конкретного клиента.

Я сделал очень похоже на вторую часть публикации OP, за исключением того, что я поместил базовый вызов в собственный метод, который я вызывал из метода Say ().

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

        BaseSay();
    }

    protected virtual void BaseSay()
    {
        base.Say();
    }
}

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

Вы можете повторять это до бесконечности, давая, например, SpecialDerived метод BaseBaseSay(), если вам нужно переопределить ExtraSpecialDerived для SpecialDerived.

Самое приятное в этом то, что если Derived меняет свое наследование с Base на Base2, все остальные переопределения следуют этому примеру без необходимости изменений.

Если вы хотите получить доступ к данным базового класса, вы должны использовать ключевое слово "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;
        }
    }
}
Другие вопросы по тегам