Обоснование дизайна общедоступного невиртуального члена суперкласса, мешающего переопределению в подклассе

В чем заключается конструктивное обоснование этого:

OK:

public class A
{
    public virtual void DoWork() { Console.WriteLine("A"); }
}
public class B : A
{
    private new void DoWork() { Console.WriteLine("B"); } //private works
}
public class C : B
{
    public override void DoWork() { Console.WriteLine("C"); }
}

НЕПРАВИЛЬНЫЙ:

public class A
{
    public virtual void DoWork() { Console.WriteLine("A"); }
}
public class B : A
{
    public new void DoWork() { Console.WriteLine("B"); } //public doesn't
}
public class C : B
{
    public override void DoWork() { Console.WriteLine("C"); }
}

Почему не виртуальный метод все равно мешает переопределению виртуального метода?

РЕДАКТИРОВАТЬ: Больше объяснений. 1-й случай: компилятор проверяет, что метод является закрытым (новым), поэтому он позволяет классу C.DoWork() переопределить класс A.DoWork() (не смешивает виртуальные и невиртуальные (B.DoWork()) методы). 2-й случай: компилятор видит, что объявлен public (new) void и произвольно(?) прерывает переопределение в классе C. Теперь, если бы я хотел объявить новый виртуальный DoWork(), я мог бы это сделать, если бы хотел запретить переопределение, Я мог бы использовать запечатанный спецификатор. Но в этом случае я написал public void DoWork() как объявление обычного невиртуального метода, и я не ожидаю, что он будет участвовать в цепочке виртуального наследования, как в частном случае. Во втором примере я ожидаю:

A ac = new C();
ac.DoWork();

вывести C как в частном случае.


person user206334    schedule 25.01.2013    source источник


Ответы (3)


Потому что в первом случае вы переопределяете A функцию класса, а не B одну

public class A
{
    public virtual void DoWork() { Console.WriteLine("A"); }
}
public class B : A
{
    private new void DoWork() { Console.WriteLine("B"); } //private works
}
public class C : B
{
    public override void DoWork() { Console.WriteLine("C"); } //OVERRIDES A.DoWork()
}

Вы не можете переопределять не virtual/abstract методы базового класса.

person Tigran    schedule 25.01.2013

В первом примере ваш класс C переопределяет класс DoWork класса A (у B был DoWork, который является закрытым и невидимым для подклассов). Во втором примере вы скрываете DoWork не виртуальной функцией, а компилятор жалуется, что вы пытаетесь переопределить не виртуальную функцию. Решение состоит в том, чтобы раскрыть семейства виртуальных функций. К new virtual вы можете создать разделение между этими семьями.

public class B : A
{
    public new virtual void DoWork() { Console.WriteLine("B"); } //private works
}

Затем этот код

A a = new C();
a.DoWork();

печатает A

B b = new C();
b.DoWork();

печатает C

Подробнее об использовании new virtual можно прочитать в Знать, когда использовать переопределение и новые ключевые слова. Ключевое слово new без virtual имеет семантику, отличную от new virtual, вы можете узнать больше об этом в новое ключевое слово в методе подпись.

person Ilya Ivanov    schedule 25.01.2013
comment
Вы упускаете из виду, что внутри среднего (B) класса НЕТ виртуального. Да, new virtual в C#, в отличие от C++, завершает цепочку виртуальной диспетчеризации, но в данном случае b.DoWork() вообще не виртуальная. - person user206334; 25.01.2013
comment
Таким образом, базовый класс имеет ключевое слово virtual, поэтому DoWork является виртуальным и не может быть скрыт в подклассах без спецификатора virtual. - person Ilya Ivanov; 25.01.2013

В приведенном ниже коде

public class A
{
    public virtual void DoWork() { Console.WriteLine("A"); }
}
public class B : A
{
    private new void DoWork() { Console.WriteLine("B"); } //private works
}
public class C : B
{
    public override void DoWork() { Console.WriteLine("C"); }
}

вы сообщаете компилятору, что метод DoWork() в классе B является new версией метода DoWork() класса A. Однако он позволяет вам выполнять override DoWork в C из-за того, что вы объявили new версию DoWork как частную в B, что заставляет компилятор предположить, что этот метод не открыт для внешнего мира.

Однако в этом,

public class A
{
    public virtual void DoWork() { Console.WriteLine("A"); }
}
public class B : A
{
    public new void DoWork() { Console.WriteLine("B"); } //public
}
public class C : B
{
    public override void DoWork() { Console.WriteLine("C"); }
}

Вы сообщаете компилятору override новую версию метода DoWork(), что немного неправильно с точки зрения компилятора, поскольку он не может override использовать не abstract/virtual/overriden метод в соответствии с правилами, установленными при разработке языка.

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

person mihirj    schedule 25.01.2013