Почему я не могу переопределить приватного члена во вложенном классе? - PullRequest
2 голосов
/ 29 января 2012

В 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 #, и только сообщение об ошибке неверно? Или это должно быть разрешено?

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

Ответы [ 3 ]

3 голосов
/ 29 января 2012

Нет такой вещи как 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 применяется к обоим методам доступа к свойству чтения-записи - виртуальный может быть только один метод доступа к свойству чтения-записи.

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

Для получения дополнительной информации о private virtual методах см. этот ответ от Эрика Липперта .

3 голосов
/ 29 января 2012

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

Нельзя использовать виртуальный модификатор с модификаторами static, abstract, private или override. virtual (C # Reference)

Также см. Можно ли переопределить частные виртуальные методы? .

1 голос
/ 29 января 2012

По определению 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, что "Iхочу свойство 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 применяются как к методам получения, так и к методам установки, но не к самому свойству.

Теперь, изменив код C # на:

public virtual object Property { get; private set; }

Вы сказали .NET, что хотите, чтобы ваш получатель получилnd методы установки должны быть виртуальными ... однако , затем он переходит в 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: «Только я и производные классы могут видеть или получать к нему доступ,и производные классы могут переопределять это свойство. "

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

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

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

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

Надеюсь, это имеет больше смысла ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...