вместо

interface IBase
{
    string Name { get; }
}

class Base : IBase
{
    public Base() => this.Name = "Base";
    public string Name { get; }
}

class Derived : Base//, IBase
{
    public Derived() => this.Name = "Derived";
    public new string Name { get; }
}


class Program
{
    static void Main(string[] args)
    {
        IBase o = new Derived();
        Console.WriteLine(o.Name);
    }
}

м случае выводом будет «База».

Если я прямо заявляю, что Derived реализует IBase (который на самом деле уже реализован базовым классом Base, и такие аннотации кажутся бесполезными), вывод будет "Derived"

class Derived : Base, IBase
{
    public Derived() => this.Name = "Derived";
    public new string Name { get; }
}

В чем причина такого поведения?

VS 15.3.5, C # 7

 Henk Holterman03 окт. 2017 г., 11:43
ВIBase o = new Derived(); у компилятора есть 2 варианта, он выбирает лучшее совпадение.
 Reniuz03 окт. 2017 г., 11:41
Важна инициализация вложенного класса
 James Thorpe03 окт. 2017 г., 11:40
Ты понимаешь чтоpublic new string Name { get; } делает дляName наBase?
 yevgenijz03 окт. 2017 г., 11:40
Ожидания - тот же результат, те же члены получили доступ. Я не понимаю, почему добавление интерфейса в определение класса, когда такой интерфейс уже реализован базовым классом, может что-то изменить.
 Henk Holterman03 окт. 2017 г., 11:36
Почему он должен вести себя иначе? Каковы ваши ожидания?

Ответы на вопрос(2)

Решение Вопроса

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

13.4.4 Отображение интерфейса

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

Отображение интерфейса для класса или структурыC находит реализацию для каждого члена каждого интерфейса, указанного в списке базовых классовC, Реализация конкретного члена интерфейсаI.M, гдеI это интерфейс, в котором членM объявлен, определяется путем изучения каждого класса или структурыS, начиная сC и повторяя для каждого последующего базового классаCдо совпадения:

ЕслиS содержит объявление явной реализации элемента интерфейса, которая соответствуетI а такжеMто этот член является реализациейI.M.В противном случае, еслиS содержит объявление нестатического открытого члена, соответствующего M, тогда этот член является реализациейI.M, Если более чем один член соответствует, не определено, какой из членов является реализациейI.M, Такая ситуация может возникнуть, только еслиS это составной тип, в котором два члена, объявленные в универсальном типе, имеют разные сигнатуры, но аргументы типа делают их сигнатуры идентичными.

...

13.4.5 Наследование реализации интерфейса

Класс наследует все реализации интерфейса, предоставляемые его базовыми классами. Без явной повторной реализации интерфейса производный класс никоим образом не может изменить отображения интерфейса, которые он наследует от своих базовых классов. Например, в объявлениях

interface IControl
{
    void Paint();
}
class Control: IControl
{
    public void Paint() {...}
}
class TextBox: Control
{
    new public void Paint() {...}
}

Paint метод вTextBox прячетPaint метод вControl, но это не меняет отображениеControl.Paint наIControl.Paintи призываетPaint через экземпляры классов и экземпляры интерфейса будут иметь следующие эффекты

Control c = new Control();
TextBox t = new TextBox();
IControl ic = c;
IControl it = t;
c.Paint();            // invokes Control.Paint();
t.Paint();            // invokes TextBox.Paint();
ic.Paint();           // invokes Control.Paint();
it.Paint();           // invokes Control.Paint();

...

13.4.6 Переопределение интерфейса

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

Повторная реализация интерфейса следует точно таким же правилам отображения интерфейса, что и первоначальная реализация интерфейса. Таким образом, унаследованное отображение интерфейса никак не влияет на отображение интерфейса, установленное для повторной реализации интерфейса. Например, в объявлениях

interface IControl
{
    void Paint();
}
class Control: IControl
{
    void IControl.Paint() {...}
}
class MyControl: Control, IControl
{
    public void Paint() {}
}

дело в том, чтоControl картыIControl.Paint наControl.IControl.Paint не влияет на повторную реализацию вMyControlкакие картыIControl.Paint наMyControl.Paint.

 Jon Skeet03 окт. 2017 г., 12:56
@ nl-x: он не сопоставлен обоим одновременно - сопоставление выполняется заново с нуля дляDerived потому что он явно говорит, что он реализует интерфейс. Вы можете думать, что у каждого класса есть полное отображение для всех интерфейсов, которые он реализует, включая унаследованные; если класс не объявляет, что он реализует интерфейс, отображение наследуется от базового класса.
 nl-x03 окт. 2017 г., 12:23
Я не должен был ссылаться на (время выполнения) порядок выполнения конструкторов, что сбивает с толку. Но если я правильно понял, член интерфейса сопоставляется как члену родителя, так и члену дочернего класса. Перезаписано ли отображение или оба сопоставления существуют, и дочернее отображение «выигрывает» при вызове?
 yevgenijz03 окт. 2017 г., 11:54
Спасибо за такой расширенный ответ. Смущает то, что можно повторно реализовать интерфейс без явной (синтаксической) реализации интерфейса в любом из двух классов (Base, Derived), просто с помощью mixin в интерфейсе для определения класса.
 Jon Skeet03 окт. 2017 г., 11:54
@ nl-x: я имею в виду, что отображение интерфейса выполняется при компиляцииBase, а затем снова при компиляцииDerived, Это выбор во время компиляции, поэтому он не имеет порядка времени выполнения ... Я не уверен, о чем вы спрашиваете.
 nl-x03 окт. 2017 г., 11:51
Ваше введение более объяснительно, чем цитаты. Но вопрос, когда вы говоритеthat triggers interface mapping again, что значитagain значит? В каком порядке осуществляется взаимодействие, а не от потомка к родителю (родителям), как порядок запуска конструкторов? То есть первое отображение интерфейса просто «выигрывает»?

Derived не реализуетIBase и заявляетnew string Name, это означает, чтоDerived.Name а такжеIBase.Name логически не то же самое. Итак, когда вы получаете доступIBase.Name, он ищет это вBase класс, реализующийIBase, вы удалитеnew string Name свойство, результат будетDerivedпотому что сейчасDerived.Name = Base.Name = IBase.Name, вы реализуетеIBase явно, вывод будетDerivedпотому что сейчасDerived.Name = IBase.Name, вы бросилиo вDerived, вывод будетDerived, потому что теперь вы получаете доступDerived.Name вместоIBase.Name.

Ваш ответ на вопрос