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

У меня есть следующий код:

public class Parent
{
    public string MyField { get; set; }
}

public class Child : Parent
{
    protected new int MyField { get; set; }
}

Я пытаюсь получить доступ к этому с:

static void Main(string[] args)
{
    Child child = new Child();
    child.MyField = "something";
}

Visual studio 2008 компилирует это без комментария, но под Моно (2.4.2, Ubuntu) я получаю сообщение об ошибке

'HideTest.Child.MyField' is inaccessible due to its protection level (CS0122)

Одна реализация или другое более совместимое со стандартом здесь?

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

29
задан Tim Martin 16 May 2010 в 23:05
поделиться

7 ответов

Из ECMA-334 (спецификация C#) §10.7.1.2 :

Объявление нового члена скрывает унаследованный член только в области видимости нового члена.

Вы можете увидеть такое поведение, выполнив этот тест на реализации Microsoft.

using System;
using NUnit.Framework;

namespace ScratchPad
{
    [TestFixture]
    public class Class1
    {
        [Test]
        public void InheritanceHiding()
        {
            var b = new Base();
            var d = new Derived();

            var baseSomeProperty = b.SomeProperty;
            var derivedSomeProperty = d.SomeProperty;

            b.GetSomeProperty();
            d.GetSomeProperty();
        }
    }

    public class Base
    {
        public string SomeProperty
        {
            get
            {
                Console.WriteLine("Getting Base.SomeProperty");
                return "Base.SomeProperty";
            }
        }

        public string GetSomeProperty()
        {
            return SomeProperty;
        }
    }

    public class Derived : Base
    {
        protected new int SomeProperty
        {
            get
            {
                Console.WriteLine("Getting Derived.SomeProperty");
                return 3; //Determined by random roll of the dice.
            }
        }

        public new int GetSomeProperty()
        {
            return SomeProperty;
        }
    }
}

Который выведет:

Getting Base.SomeProperty    //(No Controversy)  
Getting Base.SomeProperty    //(Because you're calling from public scope and the new member is in protected scope, there is no hiding)  
Getting Base.SomeProperty    //(No Controversy)  
Getting Derived.SomeProperty //(Now because you're calling from protected scope, you get the protected member).

Таким образом, свойство, к которому вы обращаетесь из вашего Main(), должно быть свойством базового класса (как в MS.NET), а не производного (как в Mono), потому что новый производный член скрывает "старый" базовый член только в защищенной области видимости.

Mono делает что-то не так, согласно спецификации.

25
ответ дан 28 November 2019 в 01:32
поделиться

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

Относительно ошибки: в CLR есть много действительно странных правил «крайнего случая» для работы с подобными вещами. Лучшее место для поиска такого рода материалов обычно - блог Эрика Липперта .

Тем не менее, я считаю, что моно делает здесь более разумную вещь.


На второй взгляд, вариант C # становится более понятным, если учесть «закулисные» вещи.

Свойства не являются «первоклассными» в MSIL. Свойство в C # или VB просто компилируется до метода получения и установки (компилятор также прикрепляет атрибут где-нибудь для учета).

int MyField {получить; установленный; } фактически создаст MSIL для двух методов:

void set_MyField(int value);
int get_MyField();

Теперь, учитывая, что ваш метод new имеет другой тип, вы получите следующие 2 метода установки.

void set_MyField(int value);
void set_MyField(string value);

Когда вы вызываете x.MyField = "string" , вы просто вызываете один из этих методов. Затем это сводится к обычному сценарию перегрузки метода. Совершенно допустимо иметь два метода с одним и тем же именем, которые принимают разные параметры, поэтому компилятор просто выберет строковый и продолжит весело.

Так что да. Вариант C # имеет смысл, если вы знаете, как работают внутренние компоненты, вариант Mono имеет больше смысла, если вы этого не сделаете.

Какой из них «более правильный»? Спросите Эрика Липперта: -)

3
ответ дан 28 November 2019 в 01:32
поделиться

В общем, .NET-реализацию C #, вероятно, следует считать "канонической". Из документации по новому модификатору :

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

... похоже, что реализация Mono более правильна с этим определением. Он должен скрывать реализацию MyField в классе Parent , и поэтому он должен быть доступен только с подписью int MyField из Child класс.

5
ответ дан 28 November 2019 в 01:32
поделиться

Вы делаете то, что доступно через базовый класс, недоступным через дочерний класс. Вы можете попробовать это, но на самом деле это ничего не даст. Люди всегда могут просто сделать это:

Parent child = new Child();

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

0
ответ дан 28 November 2019 в 01:32
поделиться

Ответ Джейсона правильный, но он просит оправдать такое поведение. (А именно, что метод сокрытия скрывается только в рамках метода скрытия.)

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

FooCorp создает Foo.DLL:

public class Foo
{
    public object Blah() { ... }
}

BarCorp создает Bar.DLL:

public class Bar : Foo
{
    // stuff not having to do with Blah
}

ABCCorp создает ABC.EXE:

public class ABC
{
    static void Main()
    {
        Console.WriteLine((new Bar()).Blah());
    }
}

Теперь BarCorp говорит: «Вы знаете, в нашем внутреннем коде мы можем гарантировать, что Blah всегда возвращает только строку. благодаря нашему знанию производной реализации. Давайте воспользуемся этим фактом в нашем внутреннем коде ».

public class Bar : Foo
{
    internal new string Blah()
    {
        object r = base.Blah();
        Debug.Assert(r is string);
        return (string)r;
    }
}

ABCCorp берет новую версию Bar.DLL, в которой есть множество исправлений ошибок, которые блокируют их. Должна ли их сборка прерваться из-за вызова Blah, внутреннего метода на Bar ? Конечно, нет. Это было бы ужасно . Это изменение является частной деталью реализации, которая должна быть невидимой за пределами Bar.DLL.

18
ответ дан 28 November 2019 в 01:32
поделиться

IMHO разница в том, что MS.NET распознает тип string для MyField и устанавливает значение свойства Parent, а в Mono просто пытается получить доступ к MyField в классе Child.

0
ответ дан 28 November 2019 в 01:32
поделиться

Просто добавляю мои 2 цента) Это ошибка Mono, здесь - описание.

2
ответ дан 28 November 2019 в 01:32
поделиться
Другие вопросы по тегам:

Похожие вопросы: