Подтвердить что ты не робот

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

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

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 компилирует это без комментариев, но в Mono (2.4.2, Ubuntu) я получаю сообщение об ошибке

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

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

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

4b9b3361

Ответ 1

От ECMA-334 (спецификация С#) §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 делает что-то неправильно здесь в соответствии со спецификацией.

Ответ 2

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

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

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, которая содержит множество исправлений ошибок, которые блокируют их. Должны ли они сломаться, потому что у них есть призыв к Блаху, внутренний метод на Бар? Конечно нет. Это было бы ужасно. Это изменение - частная реализация, которая должна быть невидимой вне Bar.DLL.

Ответ 3

В целом, реализация С#.NET должна, вероятно, считаться "каноном". Из документации по новый модификатор:

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

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

Ответ 4

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

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

Говоря, что, похоже, моно на самом деле делает более разумную вещь, на мой взгляд.


Во втором взгляде, С# один имеет больше смысла, когда вы учитываете вещи "за кулисами".

Свойства не являются "первоклассными" в MSIL. Свойство в С# или VB просто скомпилировано до метода get и set (компилятор также привязывает атрибут где-то для ведения бухгалтерии).

int MyField { get; set; } будет фактически производить MSIL для двух методов:

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

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

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

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

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

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

Ответ 5

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

Ответ 6

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

Ответ 7

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

Parent child = new Child();

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