Почему C# позволяет мне объявить реализацию интерфейса по умолчанию как запечатанную, но затем ведет себя непоследовательно, когда я это делаю?

Новое консольное приложение C# .NET Core:

using System;

namespace Interface_Sealed
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(new C().X);
            Console.WriteLine((new C() as I).X);
            Console.ReadKey();
        }

        interface I
        {
            sealed int X => 0;
        }

        class C: I
        {
            public int X => 1;
        }
    }
}

При запуске выводит на консоль "10". Первый ".X" вызывает реализацию X в классе C, а второй вызывает реализацию по умолчанию на интерфейсе I.

Почему C# позволяет мне объявить реализацию X по умолчанию как «запечатанную», а затем разрешить мне переопределить ее другой реализацией без жалоб? Почему имеет значение, получаю ли я доступ к X через ссылку типа I или типа C?

NB. Если вы реализуете I.X в C явно, а не неявно, вы получите CS0539 «'Program.C.X' в явном объявлении интерфейса не найден среди элементов интерфейса, которые могут быть реализованы». На странице документации Microsoft по этой ошибке указано, что это происходит, когда «предпринята попытка явно объявить член интерфейса, который не существует». Ну, это неправда, не так ли? Он существует.


person Hammerite    schedule 20.05.2020    source источник
comment
Что произойдет, если вы сделаете это с помощью метода, а не свойства?   -  person Joe Sewell    schedule 21.05.2020
comment
@JoeSewell Все будет по тем же причинам.   -  person Servy    schedule 21.05.2020
comment
@Joe Sewell - Если вы измените объявления и вызовы X на объявления / вызовы методов, добавив пары квадратных скобок в соответствующих местах, вы получите такое же поведение, как уже описано.   -  person Hammerite    schedule 21.05.2020


Ответы (1)


Почему C# позволяет мне объявить реализацию X по умолчанию как «запечатанную», а затем разрешить мне переопределить ее другой реализацией без жалоб?

Это не так. Вы не переопределили члена, а просто создали нового члена с тем же именем. Об этом свидетельствует тот факт, что член, который возвращает 1 не вызывается, когда тип является типом интерфейса. Это полностью соответствует запечатанным членам в классах, наследуемых от другого. Вы не запрещаете классу, реализующему интерфейс, иметь собственный член с таким именем и подписью, вы просто запрещаете ему быть тем, который связан с интерфейсом (или базовым классом).

Почему имеет значение, получаю ли я доступ к X через ссылку типа I или типа C?

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

Ну, это неправда, не так ли?

Нет, это правда. Этот элемент нельзя реализовать, поскольку он запечатан. Член есть, но вы не можете изменить его реализацию, отсюда и ошибка.

person Servy    schedule 20.05.2020
comment
Что касается вашего последнего абзаца: нет, это действительно неправда. Утверждение, которое является референтом этого, неверно в моем вопросе, - это утверждение со страницы документации Microsoft, на которую ссылается, что ошибка возникает, когда элемент интерфейса не существует - и он существует. Ваш последний абзац предполагает, что вместо этого я утверждаю, что это неправда, что член интерфейса не может быть реализован; Я не делаю такого заявления. - person Hammerite; 21.05.2020
comment
@Hammerite Не удается найти среди элементов интерфейса, которые можно реализовать. Это член, который не может быть реализован, поэтому его нельзя найти при поиске члена для явной реализации. Конечно, наиболее распространенная причина, по которой метод отсутствует в списке методов, которые могут быть реализованы, заключается в том, что его вообще нет в интерфейсе, а член, который не может быть реализован, является гораздо менее распространенным случаем. - person Servy; 21.05.2020
comment
Но он существует, так что я прав, когда замечаю, что неверно говорить, что его не существует. - person Hammerite; 21.05.2020
comment
@Hammerite Да, он существует. Но ошибка правильная, когда говорится, что она не существует среди элементов интерфейса, которые могут быть реализованы. Эта квалификация очень актуальна. - person Servy; 21.05.2020
comment
Мне все равно, что написано в сообщении об ошибке. Меня волнует, что говорится в отрывке, который я процитировал на странице документации Microsoft (который не содержит вашей выделенной курсивом квалификации). Этот отрывок является референтом того, что в моем комментарии не соответствует действительности. - person Hammerite; 21.05.2020
comment
Важнее. Почему C# вообще позволяет мне объявить элемент интерфейса запечатанным? Если вы объявляете член класса запечатанным, когда он ничего не переопределяет, вы получаете CS0238... не может быть запечатан, потому что это не переопределение. - person Hammerite; 21.05.2020
comment
Члены @Hammerite в классах запечатаны, если они явно не сделаны виртуальными, члены в интерфейсах являются виртуальными, если они явно не запечатаны. Тот факт, что ваша программа не будет вести себя так же, если вы удалите sealed, говорит вам, что она не избыточна, поэтому она позволяет вам запечатать ее. - person Servy; 21.05.2020
comment
Несоответствия/ясность в формулировках сообщений об ошибках и документации, вероятно, возникают из-за того, что новая функция в основном разрешает (почти?) все модификаторы в интерфейсах, которые никогда не были разрешены ранее (статические, переопределение, запечатанные, частные и т. д.). Документ и сообщения просто не полностью соответствуют истинному положению вещей. Я вспоминаю проблему с github, когда даже кто-то из команды c# не осознавал, что конкретный модификатор разрешен, пока до них не дошло, что теперь они разрешают все модификаторы, что также частично отвечает на ваш вопрос о том, почему запечатанный допускается - person pinkfloydx33; 21.05.2020