Подтвердить что ты не робот

Каков прецедент для этой наследования?

При наследовании унаследованного класса поведение new/override не то, что я ожидаю:

$ cat Program.cs
using System;

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

public class Program {
    public static void Main() {
        A p = new C();
        p.SayHi();
    }
}

$ ./Program.exe 
From A

Поскольку класс C переопределяет метод sayHi(), я ожидаю, что результат будет From C. Почему модификатор класса B new имеет приоритет здесь? Что для этого используется? Тем более, что он нарушает очевидный вариант использования C, который действительно отменяет A.

Обратите внимание, что приведенный выше код был запущен в Mono 2.10, запущенный в дистрибутиве Debian. Но я подтвердил то же поведение, используя компилятор С# в MS Visual Studio.

4b9b3361

Ответ 1

new модификатор заставляет скрыть элемент, который разбивает полиморфные отношения в вашей иерархии классов. Метод SayHi B рассматривается как отличный (не переопределяющий) из A s (таким образом, выбор слова "новый" как ключевое слово). C s затем переопределяет B s, а не A s (который остается скрытым).

Поэтому, когда вы вызываете SayHi в экземпляре C с помощью ссылки A, среда выполнения будет разрешать его по типу A, а не по типу C (внутри которого SayHi является "новый" метод, унаследованный от B).

Если, с другой стороны, вы должны были запустить:

B p = new C();
p.SayHi();

... вы получите ожидаемый полиморфный результат:

From C

Изменить. Так как вы запросили прецедент, то один. До появления родословных в .NET Framework 2.0 иногда сглаживание элементов использовалось как средство изменения возвращаемых типов унаследованных методов в производных классах (что нельзя делать при переопределении), чтобы возвращать более конкретные типы. Например:

class ObjectContainer
{
    private object item;

    public object Item 
    {
        get { return item; }
        set { item = value; }
    }
}

class StringContainer : ObjectContainer
{
    public new virtual string Item
    {
        get { return base.Item as string; }
        set { base.Item = value as string; }
    }
}

class QuotedStringContainer : StringContainer
{
    public override string Item
    {
        get { return "\"" + base.Item + "\""; }
    }
}

Свойство Item класса ObjectContainer возвращает простой object. Однако в StringContainer это унаследованное свойство скрыто для возврата a string. Таким образом:

ObjectContainer oc = new StringContainer();
object o  = oc.Item;   // Valid, since ObjectContainer.Item is resolved
string s1 = oc.Item;   // Not valid, since ObjectContainer.Item is still resolved
string s2 = ((StringContainer)oc).Item;   
                       // Valid, since StringContainer.Item is now resolved

Класс QuotedStringContainer переопределяет свойство Item StringContainer, наследуя его возвращаемый тип string; однако он все еще скрыт от свойства object -returning Item ObjectContainer. Если бы это было не так, не было бы способа примирить их разрозненные типы возврата...

ObjectContainer oc = new QuotedStringContainer();
object o  = oc.Item;   // Valid, since ObjectContainer.Item is resolved
string s1 = oc.Item;   // Not valid, since ObjectContainer.Item is still resolved
string s2 = ((StringContainer)oc).Item;   
                       // Valid, since QuotedStringContainer.Item is now resolved
                       // (polymorphism!)
string s3 = ((QuotedStringContainer)oc).Item;   
                       // Valid, since QuotedStringContainer.Item is now resolved

Ответ 2

C переопределяет затененную версию метода (которая shadowed in B), а не переопределять значение в A.

В результате, когда вы используете переменную типа A, вызывается SayHi, определенная в A, поскольку она не переопределяется в C.

Ответ 3

C переопределяет метод B, поэтому, когда вы передаете его в A, вы вызываете виртуальную машину, определенную в A.

См. ECMA 334: Спецификация языка С# в разделе 17.5.3 в значительной степени соответствует вашему примеру (стр. 294).

Ответ 4

Поскольку класс C не переопределяет метод SayHi в классе A, он переопределяет "новый" метод в B. Поскольку ваш приведение относится к A, компилятор разрешает это как вызов A.SayHi() чем C.SayHi()

Ответ 5

Последний пример на этой странице msdn подробно объясняет, что здесь происходит.

В основном новый модификатор заставляет метод в быть скрытым от C, и поскольку он является общедоступным, когда C переопределяет его, он переопределяет метод из B (который рассматривается как его собственный отдельный метод).

Если вы установите метод в B в закрытый, C снова переопределит метод в A.

class B : A
{
    private new void SayHi()
    {
        Console.WriteLine("From B");
    }
}

Результаты в: From C