Как вызвать 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();
    }
}

Результат:

Вызывается из специального производного.
Вызывается из производного. / * этого не ожидается * /
Вызывается с базы.

Как я могу переписать класс SpecialDerived так, чтобы метод среднего класса "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.");
    }
}

person AZ.    schedule 24.02.2010    source источник
comment
Редактирование в соответствии с вашим обновлением.   -  person JoshJordan    schedule 24.02.2010
comment
comment
Это работает так, как ожидалось, и должно быть. Кроме того, ваш заголовок вводит в заблуждение. На самом деле вы спрашиваете, как избежать вызова base.base.method при вызове base.base.base.method ?.   -  person Jason Cheng    schedule 13.05.2021


Ответы (11)


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

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();            
    }
}
person Evk    schedule 14.09.2015
comment
Мне очень нравится этот ответ, потому что вопрос был не в том, рекомендуется ли это или хорошая идея, а в том, есть ли способ сделать это, и если да, то какой. - person Shavais; 09.04.2016
comment
Особенно полезно, когда вам приходится иметь дело с фреймворками / библиотеками, которым не хватает расширяемости. Например, мне никогда не приходилось прибегать к подобным решениям для NHibernate, который обладает высокой расширяемостью. Но для работы с Asp.Net Identity, Entity Framework, Asp.Net Mvc я регулярно использую такие хаки для обработки их недостающих функций или жестко запрограммированного поведения, неподходящего для моих нужд. - person Frédéric; 20.07.2016
comment
Спасибо за это! Я хотел бы попросить других прекратить цитировать инструкции в ответ на вопрос. Спросили по причине, а не для ругательства. Если не знаете, не отвечайте! Я также хотел бы призвать всех взорвать кодовую полицию, чтобы, возможно, это помешало им публиковать ответы, не отвечающие. Если после ответа на вопрос вы чувствуете себя обязанным процитировать рекомендации, укажите это. - person DanW; 15.12.2017
comment
Что, если нам нужно возвращаемое значение из базовой функции? - person Perkins; 01.09.2018
comment
Да ладно, разобрался. Транслировать в Func<stuff> вместо Action - person Perkins; 01.09.2018
comment
Спасибо. Мы все знаем, что такого рода вещи - плохая игра, но иногда вам просто нужно это делать, потому что это лучше, чем альтернатива (переписывание чьей-либо прекрасной библиотеки). - person satnhak; 10.10.2018
comment
В моддинге довольно часто игнорируют все архитектурные проблемы, потому что часто это единственный способ решить проблему. Мне нравится этот ответ, потому что он действительно решает проблему. Но будьте осторожны, я создал следующую проблему, которая была исправлена ​​недавно и может не существовать в вашей среде выполнения (пока): github.com/mono/mono/issues/19964 - person Andreas Pardeike; 10.07.2020
comment
Прекрасный пример того, где нам это нужно: Android требует, чтобы производные объекты представления вызывали их базовый / родительский / супер-метод OnRestoreInstanceState. По-видимому, это где-то наверху иерархии. Если этого не сделать, запускается исключение. Если вы производите от класса (который вы не можете изменить, но МОЖЕТЕ переопределить) с ошибочной реализацией этого метода, вам необходимо переопределить и обойти своего непосредственного родителя, но все же необходимо вызвать метод base.base. - person Josh Sutterfield; 17.07.2020
comment
Как ни странно, в моем случае этот подход закончился повторным вызовом только производного метода. - person Josh Sutterfield; 17.07.2020

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

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

  • Чтобы проиллюстрировать конкретный пример предыдущего пункта: если он разрешен, этот шаблон был бы еще одним способом сделать код уязвимым для сбоев хрупкого базового класса. Предположим, что C происходит от B, которое происходит от A. Код в C использует base.base для вызова метода A. Затем автор B понимает, что они вложили слишком много снаряжения в класс B, и лучший подход - создать промежуточный класс B2, который является производным от A, а B является производным от B2. После этого изменения код в C вызывает метод в B2, а не в A, потому что автор C сделал предположение, что детали реализации B, а именно, что его прямой базовый класс A, никогда не изменится. Многие проектные решения в C # призваны снизить вероятность различных видов хрупких отказов основания; решение сделать base.base незаконным полностью предотвращает эту особую разновидность этой схемы отказа.

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

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

person Eric Lippert    schedule 24.02.2010
comment
Что касается безопасности, следует отметить, что вы не можете гарантировать, что вызывающие абоненты вызовут наиболее производную версию виртуальной функции. - person rh.; 24.02.2010
comment
@rh: да, это хороший аргумент. Враждебный код не обязательно подчиняется правилам C #. - person Eric Lippert; 24.02.2010
comment
@rh: На самом деле, до .NET 2.0 было хуже; раньше в IL было законным вызывать переопределенные методы базового класса, даже если вызывающий класс не был производным от него. См. research.microsoft.com/en-us /um/people/akenn/sec/appsem-tcs.pdf для комментариев о том, как это и другие несоответствия IL / C # влияют на безопасность кода C #. - person kvb; 24.02.2010
comment
+1 ООП предназначен как раз для повторного использования базового поведения базового класса. Если кто-то предпочитает делать то, что делает класс base.base, то производный от него, а не от его производного. знак равно - person Will Marcouiller; 25.02.2010
comment
@kvb: Отличный момент. Фактически, это изменение было внесено прямо перед выпуском 2.0, и оно нарушило проверяемость базовых вызовов в анонимных методах в C #. Как вы заметили, верификатор проверяет, является ли сайт вызова производным от типа, объявляющего метод; он не проверяет, содержится ли сайт вызова в типе, объявляющем метод! К счастью, генератор кода теперь наконец-то делает правильные вещи, и мы устранили предупреждение о том, что базовый вызов не поддается проверке. - person Eric Lippert; 25.02.2010
comment
Что делать, если я не знаю, какие функции мне нужны (Base или GrandBase) до момента выполнения? У нас нет концепции наследования во время выполнения в C # - person Haseeb Jadoon; 29.12.2015
comment
@Jadoon: Тогда предпочитаю композицию наследованию. Ваш класс может принимать экземпляр Base или GrandBase, а затем класс может передать функциональность экземпляру. - person Eric Lippert; 29.12.2015
comment
@BlackOverlord: Поскольку вы твердо придерживаетесь этой темы, почему бы не написать собственный ответ на этот вопрос семилетней давности? Таким образом, мы все извлечем пользу из вашей мудрости по этому поводу, и тогда вы бы написали два ответа на StackOverflow, удвоив свой общий вклад. Это беспроигрышный вариант. - person Eric Lippert; 02.05.2017
comment
@ Эрик Липперт: Я не написал свой ответ по двум причинам: во-первых, я не знал, как это сделать, поэтому я нашел эту тему. Во-вторых, на этой странице есть исчерпывающий ответ Evk. - person BlackOverlord; 03.05.2017
comment
@DanW: Я знаю ответ; это первое предложение моего ответа: желаемая функция не разрешена в C #, потому что это плохая практика программирования. Как это сделать на C #? Не знаете. Мысль о том, что я, возможно, не знаю ответа на этот вопрос, забавна, но мы оставим это без дальнейших комментариев. Теперь, если вы найдете этот ответ неудовлетворительным, почему бы не написать свой собственный ответ, который, по вашему мнению, работает лучше? Таким образом, мы все учимся на вашей мудрости и опыте, и вы также удвоите количество ответов, опубликованных в этом году. - person Eric Lippert; 15.12.2017
comment
Извини, Эрик, учитывая свежесть всей цепочки, в то время мне казалось очевидным, что этот комментарий должен был накапливаться в основном ответе на эту ветку, где ничего не говорилось о том, как это сделать на самом деле, а не о твоем комментарии, где вы действительно дали полезную информацию ... - person DanW; 18.12.2017
comment
@EricLippert, а что, если базовый код ошибочен и его нужно переопределить, как сторонний элемент управления? А в реализации есть вызов grandbase? Для реальных приложений это может иметь критический характер и требовать исправления, поэтому ждать стороннего поставщика может быть не вариант. Плохая практика против реальности производственной среды. - person Shiv; 01.03.2018

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

person rh.    schedule 24.02.2010

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

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

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

person JoshJordan    schedule 24.02.2010
comment
Почему бы просто не написать защищенную функцию в Derived, которая вызывает Base.Say, чтобы ее можно было вызывать из SpecialDerived? Проще, не так ли? - person nawfal; 02.05.2014

Почему бы просто не привести дочерний класс к определенному родительскому классу и затем вызвать конкретную реализацию? Это особая ситуация, и следует использовать особое решение. Однако вам придется использовать ключевое слово 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());
    }
}
person Kruczkowski    schedule 02.10.2015
comment
Если у вас есть движок, который не может знать о базе или дочернем элементе, и говорит, чтобы он работал правильно при вызове этим движком, то Speak должен быть переопределением, а не новым. Если дочернему элементу требуется 99% функциональности базы, но в случае с одним говорящим ему нужна функциональность супербазы ... это такая ситуация, о которой, как я понимаю, говорит OP, и в этом случае этот метод не будет работать. Это не редкость, и проблемы безопасности, которые привели к поведению C #, обычно не вызывают особого беспокойства. - person Shavais; 09.04.2016
comment
Просто примечание в случае элементов управления и цепочек вызовов событий, методы часто защищены и, следовательно, недоступны в таком виде. - person Shiv; 01.03.2018
comment
Это не использует наследование, вы также можете дать каждому Speak полностью уникальное имя. - person Nick Sotiros; 14.03.2018
comment
да, это не 100% наследование, но он использует интерфейс от родителя - person Kruczkowski; 14.11.2019

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

person Rajesh    schedule 06.05.2011
comment
Именно так, и это сохраняет всю схему абстракции, о которой все так беспокоятся, и подчеркивает, что схемы абстракции иногда доставляют больше проблем, чем они того стоят. - person Shavais; 09.04.2016

Мой 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 методов доступа для облегчения этой функциональности.

person Julian Gold    schedule 16.06.2016

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

Вот пример:

//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();  
     }         
}
person yoel halb    schedule 16.06.2013

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

Кажется, есть много этих вопросов, связанных с наследованием метода-члена от класса 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 #. Я думаю, что Microsoft не очень хорошо объяснила базовое наследование. Слишком много внимания уделяется полиморфизму и реализации. Когда я копаюсь в их документации, я не вижу примеров этой очень простой идеи. :(

person Stokely    schedule 11.10.2018

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