2012-01-29 5 views
2

В C# вложенные классы могут обращаться к закрытым членам содержащего класса.Почему я не могу переопределить частный член во вложенном классе?

Почему я не могу переопределить таких членов? И почему ошибка компилятора ошибочна?

private abstract class InheritanceTest 
    { 
     public virtual object Property 
     { 
      get { return null; } 
      private set { } 
     } 

     public class Child : InheritanceTest 
     { 
      public override object Property 
      { 
       get { return null; } 
       private set { base.Property = null; } // the base.Property = null statement here is just to show that there isn't an error message for accesing a parent private member. 
      } 
     } 
    } 

сообщение только ошибка, я получаю:

'Program.InheritanceTest.Child.Property.set': не может переопределить унаследованное элемент 'Program.InheritanceTest.Property.set', потому что это не помечены как виртуальные, абстрактные или переопределенные

Компилятор явно получает что-то неправильно, потому что все свойство помечено как виртуальное. Метод get может переопределять унаследованный элемент.

Является ли эта часть спецификации C#, и только сообщение об ошибке является неправильным? Или это должно быть разрешено?

Какую часть спецификации мне не хватает? (Или компилятор не хватает?)

+1

возможно дубликат [Почему частные виртуальные методы запрещены в C#?] (Http://stackoverflow.com/questions/3082310/why-are-private-virtual- методы-незаконный-в-c) – McKay

+0

Я проголосовал за закрытие этого вопроса как дубликата. Я бы рекомендовал другим сделать то же самое. – McKay

ответ

3

Частные виртуальные методы и свойства не могут быть переопределены:

Вы не можете использовать виртуальный модификатор со статическим, реферата, частный, или переопределить модификаторы. virtual (C# Reference)

Также см Can you override private virtual methods?.

3

Нет такой вещи, как virtual private. Из-за этого аксессуар set, определяемый внешним классом, не является virtual и поэтому вы не можете его переопределить.

Для методов, спецификация явно запрещает private virtual (§10.6 из спецификации C# 4):

Если декларация включает в себя private модификатор, то объявление не включает в себя какой-либо из следующих модификаторов: virtual, override, или abstract.

Что касается свойств (§10.7):

декларации собственности подчиняются тем же правилам, что объявления метода (§10.6) в отношении допустимых комбинаций модификаторов.

Единственная часть спецификации, которая, как представляется, отношение к virtual свойствам, где один из аксессоров являются private является (§10.7.5):

Декларация virtual свойства определяет, что аксессор свойство виртуальное. Модификатор virtual применяется к обоим аксессурам свойства чтения-записи - невозможно, чтобы только один элемент доступа свойства read-write был виртуальным.

Это похоже на то, что на самом деле происходит. Если я что-то не хватает, я думаю, что это либо ошибка в документации, либо в компиляторе. Я создал Connect bug об этом, давайте посмотрим, что Microsoft должна сказать.

Для получения дополнительной информации о методах private virtual см. this answer from Eric Lippert.

+0

+1 для ссылок на спецификацию, но я думаю, что строка документации правильная. Места, в которых 'virtual' опущена, относятся к объекту non-readwrite (либо только для чтения, либо только для записи, а только 1 из его членов' virtual'). Помните, что если либо get, либо set отмечен 'private', то он больше не является' virtual', а свойство больше не «read-write», поэтому не соответствует этой строке спецификации. – CodingWithSpike

+0

Просто потому, что один из аксессуаров имеет различную доступность, не означает, что свойство больше не читает-пишет. Или, по крайней мере, я не нашел ничего в спецификации, которая бы так выразилась. – svick

+0

Ах да, я понимаю, что вы имеете в виду, извините, я думал о «только для чтения» и «только для записи» с точки зрения производного класса, а не из самого базового класса. Сожалею. В таком случае, да, я думаю, вы правы, что спецификация может быть неправильной. – CodingWithSpike

1

По определению, private членов не являются допустимыми. Если вы хотите, чтобы сеттер был перегружаемым, вы можете пометить его protected вместо private.

private abstract class InheritanceTest 
    { 
     public virtual object Property 
     { 
      get { return null; } 
      protected set { } 
     } 

     public class Child : InheritanceTest 
     { 
      public override object Property 
      { 
       get { return null; } 
       protected set { base.Property = null; } 
      } 
     } 
    } 

Чтобы более конкретно ответить на ваш вопрос, почему:

Поймите, что, когда ваш код C# компилируется в IL код, там на самом деле заканчивается время 3 вещи 1 собственности.

  1. Property Имущество непосредственно.
  2. Метод с именем get_Property(), который является геттером.
  3. Идентификатор метода set_Property(), который является установщиком.

В своем коде, вы сказали, что .NET «Я хочу virtual собственности. Затем он каскады, что уровень доступа к геттерным и инкубационным методам. На самом деле, в IL коды, свойства не уточняет virtual вообще .

Для C# код:

public virtual object Property { get; set; } 

Сформированный IL-код является:

.property instance object Property() { ... } 

.method public hidebysig newslot specialname virtual 
    instance object get_Property() cil managed 
    { ... } 

.method public hidebysig newslot specialname virtual 
    instance object set_Property() cil managed 
    { ... } 

Обратите внимание, что ключевые слова public и virtual применяются как к методу получения, так и к методу setter, но не к самому свойству.

Теперь, изменив свой код C# для:

public virtual object Property { get; private set; } 

Вы сказали .NET, что вы хотите, чтобы ваши методы получения и установки быть виртуальными ... однако, то он впадает в private set, и что уровень доступа переопределяетpublic и virtual уровень доступа, для метода сеттера. Таким образом, сгенерированный IL код становится:

.property instance object Property() { ... } 

.method public hidebysig newslot specialname virtual 
    instance object get_Property() cil managed 
    { ... } 

.method private hidebysig newslot specialname 
    instance object set_Property() cil managed 
    { ... } 

Обратите внимание, что в настоящее время set_Property()private это, и больше не virtual. На самом деле невозможно иметь private virtual в .NET, потому что это не имеет смысла ... это похоже на попытку сказать: «ни один другой класс не может это увидеть ... но производные классы могут переопределить эту вещь, видеть или получать доступ ", что не имеет смысла. Производные классы не могут переопределить то, что они даже не видят.

Ключевое слово protected является надлежащей заменой в этом случае, поскольку оно сообщает .NET. «Только я и производные классы могут видеть или получать доступ к этому, а производные классы могут переопределять это свойство».

Так что я думаю «короткий» ответ был бы только что был «, потому что это не может быть private и virtual в .NET, поэтому компилятор принимает более ограниченный уровень доступа, который вы дали его.

Кроме того, IMO сообщение об ошибке довольно правильно

«Program.InheritanceTest.Child.Property.set»:. не может переопределить унаследовал член «Program.InheritanceTest.Property.set», потому что он не отмечен виртуальное, аннотация, или переопределение

Обратите внимание, что это 'Program.InheritanceTest.Property.set', поэтому «.set» в конце ссылается на возможный метод set_Property(), а не на свойство Property. И метод set_Property() отмечен только private, потому что компилятор .NET видел это и удалил virtual из этого метода по указанной выше причине. Я полагаю, что было бы разумно иметь предупреждение о компиляторе или что-то сказать, что «виртуальный будет игнорироваться для« набора ».

Надеется, что имеет смысл ...

+0

Да, я уже это сделал, реальный вопрос: «Почему я должен?» и «Почему сообщение об ошибке менее полезно?» – McKay

+0

@McKay - Я расширил свой ответ, чтобы попытаться объяснить «почему». – CodingWithSpike

Смежные вопросы