Почему это объявление класса C # компилируется? - PullRequest
8 голосов
/ 21 июля 2009

Этот вопрос действительно бессмысленный, но мне просто любопытно:

Это:

public sealed class MyClass
{
   protected void MyMethod(){}
}

компилируется, но выдает предупреждение

пока это:

public sealed class MyClass
{
   public virtual void MyMethod(){}
}

не компилируется. Просто из любопытства, есть ли причина для этого?

Ответы [ 9 ]

12 голосов
/ 21 июля 2009

virtual используется для объявления метода / свойства "override -able".

sealed используется для объявления того, что класс не может быть унаследован от.

Таким образом, виртуальный метод в запечатанном классе никогда не может быть переопределен, поскольку класс никогда не может быть унаследован от. Это просто не имеет смысла.

protected влияет на доступ к члену, он не объявляет его «перезаписываемым», как виртуальный (хотя это часто используется таким образом) и, соответственно, не противоречит.

4 голосов
/ 21 июля 2009

Единственная причина, по которой я могу придумать, заключается в том, что иногда вам нужно писать защищенные методы для переопределения других защищенных методов. Язык мог бы быть спроектирован таким образом, чтобы:

protected override void Foo()

но не это

protected void Foo()

но за этим, возможно, было бы трудно понять - это отсутствие из override делает его бесполезным, тогда как в случае

public virtual void Foo()

бесполезно присутствие из virtual. Присутствие чего-то «неправильного», вероятно, легче понять, чем отсутствие чего-то полезного.

В этом случае виртуальность может также влиять на производительность, в то время как создание чего-то защищенного, а не частного, вероятно, не делает - так что это немного серьезнее.

Это всего лишь догадки, хотя на самом деле - если нам действительно повезет, Эрик Липперт даст более определенный ответ. Он тот, кого ты хочешь, а не я :)

Лучший ответ: воспринимать предупреждения как ошибки, и они в любом случае эквивалентны;)

4 голосов
/ 21 июля 2009

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

2 голосов
/ 21 июля 2009

Ошибка:

CS0549: 'function' - это новый виртуальный член в запечатанном классе 'class'.

Прежде всего, несмотря на тот факт, что на самом деле нет смысла включать новые protected или virtual члены в класс sealed, CLI¹ разрешает это. CLI также позволяет вызывать члены запечатанного класса с помощью инструкции callvirt IL, даже если компилятор может свободно заменить его инструкцией call.

В настоящее время я не могу найти ничего в ECMA-334 (спецификация языка C #), которая требует, чтобы компилятор выдавал вышеуказанную ошибку. Похоже, что реализация Microsoft добавила ошибку только потому, что не имеет смысла включать новые виртуальные члены в запечатанный класс.

CL CLI - это виртуальная машина, и компилятор C # выдает на ней байт-код. По этой причине почти любая концепция, которая является недопустимой в CLI, также недопустима в C #, но это тот случай, когда C # делает немного больше (не то, что это проблема).

Edit: Кажется, что посты, отмеченные как размеченные, объясняют, почему не имеет смысла писать такой код в OP. Но относительно того, какое правило сделало это ошибкой компилятора, они кажутся неправильными.

2 голосов
/ 21 июля 2009

Запечатанный класс может иметь защищенных членов посредством наследования. Когда метод является частью класса, не имеет значения, как этот метод туда попал.

В первом случае с защищенным методом в запечатанном классе он такой же, как если бы запечатанный класс наследовал защищенный метод. Так что компилируется.

Из любопытства, что именно дает предупреждение?

1 голос
/ 21 июля 2009

Запечатанный класс не может быть разделен на подклассы, поэтому виртуальный - не вариант. Таким образом ошибка.

Это первое немного глупо, но верно, поэтому предупреждение.

0 голосов
/ 08 декабря 2011

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

Что касается того, почему virtual выдает ошибку, тогда как защищенный выдает только предупреждение, я могу только предположить, что, возможно, это связано с тем фактом, что новые виртуальные методы требуют компилятора для построения структур данных для типа (vtable), тогда как новые защищенные члены имеют только установленный флаг доступа - без новой структуры данных. Если компилятору запрещено создавать виртуальные таблицы для запечатанного класса, что он должен делать, если сталкивается с новым виртуальным методом? Сбой компиляции. Новый защищенный метод в запечатанном классе не имеет смысла, но не требует, чтобы компилятор выходил на запрещенную территорию.

0 голосов
/ 08 декабря 2011

в запечатанном виде When applied to a class, the sealed modifier prevents other classes from inheriting from it.

здесь я пытаюсь объяснить вам одно за другим:

public sealed class MyClass
{
   protected void MyMethod(){}
}

это дает вам предупреждение, потому что практически это не имеет смысла, потому что после объявления класса как закрытого, вы не можете его наследовать, а ваш метод равен protected, поэтому вы не можете получить к нему доступ вне класса, используя его объект (и сохраняйте также имейте в виду, что вы не можете создать дочерний класс этого, поэтому вы не можете использовать этот метод с помощью этого трюка). Так что практически нет смысла делать его protected, так что компилятор выдаст вам предупреждение, но если вы сделаете это как public или internal, тогда это не даст вам ошибки, потому что это полезно в этом случае.

теперь второй:

public sealed class MyClass
{
   public virtual void MyMethod(){}
}

когда вы запечатали свой класс, а теперь вы делаете свой метод виртуальным, так что косвенно вы предлагаете кому-то переопределить его, и это может быть возможно только по наследству, и здесь возникает проблема. Этот класс запечатан, так что вы не может выполнить наследование с этим классом. поэтому с virtual он выдает ошибку.

Надеюсь, это поможет вам понять.

для справки http://msdn.microsoft.com/en-us/library/88c54tsw.aspx

0 голосов
/ 21 июля 2009

Я предполагаю, что компилятор выполняет некоторые оптимизации с запечатанными классами, которые невозможны, если у вас объявлен виртуальный метод - вероятным кандидатом является "отсутствие vtable".

Это всего лишь предположение.

...