C#: Любой способ пропустить один из базовых вызовов в полиморфизме?
class GrandParent
{
public virtual void Foo() { ... }
}
class Parent : GrandParent
{
public override void Foo()
{
base.Foo();
//Do additional work
}
}
class Child : Parent
{
public override void Foo()
{
//How to skip Parent.Foo and just get to the GrandParent.Foo base?
//Do additional work
}
}
Как показывает приведенный выше код, как я могу сделать так, чтобы Child.Foo() выполнял вызов GrandParent.Foo() вместо того, чтобы переходить в Parent.Foo()? base.Foo()
сначала берет меня в родительский класс
6 ответов
Ваш дизайн не так, если вам это нужно.
Вместо этого поместите логику для каждого класса в DoFoo
и не звони base.DoFoo
когда тебе не нужно
class GrandParent
{
public void Foo()
{
// base logic that should always run here:
// ...
this.DoFoo(); // call derived logic
}
protected virtual void DoFoo() { }
}
class Parent : GrandParent
{
protected override void DoFoo()
{
// Do additional work (no need to call base.DoFoo)
}
}
class Child : Parent
{
protected override void DoFoo()
{
// Do additional work (no need to call base.DoFoo)
}
}
Я думаю, что здесь что-то не так с вашим дизайном. По сути, вы хотите "нарушить" правила полиморфизма. Ты говоришь Child
должно происходить из Parent
но хочу удобно пропустить реализацию в родительском элементе.
Переосмыслите свой дизайн.
Нет, это не будет надежным в любом случае. Вы, как разработчик своего класса, можете выбрать ближайший базовый класс. Но кто скажет, что более поздний выпуск Parent
может не наследовать от ParentBase
что в свою очередь наследует от GrandParent
? До тех пор, пока Parent
все еще реализует правильный контракт, это не должно вызывать проблем для тех классов, которые наследуются от Parent
,
Нет, это невозможно. Представьте, какими безумными были бы вещи, если бы это было возможно.
Если вы хотите что-то конкретное пропущено в Child
В этом случае попробуйте переработать свой дизайн, чтобы лучше представить, что вам нужно (например, может быть, вам нужно переопределить что-то еще в Child
класс тоже). Или вы могли бы предоставить другой Foo()
в Parent
класс, который ничего не делает, кроме как назвать его base.Foo()
,
Если у вас есть контроль над кодом, самый простой способ - создать защищенный метод в родительском классе, который вызывает только base.Foo(), а реализация Foo вашего дочернего класса явно вызывает этот метод.
У нас был именно такой сценарий в большом проекте, где производные методы вызывались из разных мест. Из-за того, что сценарии управления изменениями и контроля качества не должны нарушаться, помимо прочих ограничений, "радикальный" рефакторинг и реструктуризация классов не всегда возможны в большом зрелом проекте. Также мы не хотели переопределять метод и исключать всю базовую функциональность. Большинство решений, встречающихся в других местах, выглядели немного неуклюже, но решение Джоша Джордана о том, как вызывать base.base, было весьма полезным.
Однако мы следовали подходу ниже (который, как я вижу, сейчас очень похож на предложенный Даном Абрамовым).
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.
}
}
Все эти твердые мнения...
Иногда имеет смысл использовать 99% чего-либо...
public class Base
{
public virtual void Foo()
{
// Do something
}
}
public class DerivedLevel1 : Base
{
public override void Foo()
{
DerivedLevel1Foo();
}
protected void DerivedLevel1Foo()
{
// Do something
base.Foo();
}
}
public class DerivedLevel2 : DerivedLevel1
{
public override void Foo()
{
DerivedLevel2Foo();
}
protected void DerviedLevel2Foo()
{
// Do something
base.Foo();
}
}
public class Special : Derived
{
public override void Foo()
{
// Don't do DerivedLevel2Foo()
base.DerivedLevel1Foo();
}
}