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

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

В С# мы не можем изменять модификатор доступа, переопределяя метод из базового класса. например.

Class Base
{
   **protected** string foo()
   {
       return "Base";
   }
}

Class Derived : Base
{
   **public** override string foo()
   {
       return "Derived";
   }
}

Это недействительно в С#, оно даст ошибку времени компиляции.

Я хочу знать причину, почему это не разрешено. Есть ли какая-либо техническая проблема или может привести к чему-то, что несовместимо с точки зрения ограничения доступа???

4b9b3361

Ответ 1

Изменение модификатора доступа метода в производном типе бессмысленно, поэтому его нельзя разрешить:

Случай 1: переопределение с более ограничительным доступом

Этот случай, очевидно, не допускается из-за следующей ситуации:

class Base
{
    public virtual void A() {}
}

class Derived: Base
{
    protected override void A()
}

Теперь мы могли бы сказать:

List<Base> list;
list.Add(new Derived());
list[0].A() //Runtime access exception

Случай 2: переопределение с менее ограничительным модификатором доступа

В чем смысл? Скройте метод, и все готово. Очевидно, что если кто-то звонит через базовый тип, у них не будет доступа к новому методу, определенному в производном типе, но это согласуется с тем, как автор базового типа хотел, чтобы все было так, чтобы у вас не было "права", чтобы изменить это. Если вам нужна спецификация вызова производного класса из производного класса, в этом случае метод new работает отлично.

EDIT: Расширяющийся случай 2

То, что я пытаюсь сказать в случае 2, заключается в том, что у вас уже есть средства для изменения доступности любого метода (виртуального или нет), если вы хотите изменить доступность.

Рассмотрим следующий код:

public class Base
{
    protected virtual string WhoAmI()
    {
        return "Base";
    }
}

public class Derived : Base
{
    public new virtual string WhoAmI()
    {
        return "Derived";
    }
}

public class AnotherDerived : Derived
{
    public override string WhoAmI()
    {
        return "AnotherDerived";
    }
}

С помощью ключевого слова new вы эффективно создали новый виртуальный метод для вашего класса Derived с тем же именем и подписью. Обратите внимание, что ДОЛЖНО объявить метод new virtual, поэтому любому классу, полученному из Derived, будет разрешено переопределить его.

Нельзя разрешать кому-то делать следующее:

 Base newBaseObject = new Derived();
 newBaseObject.WhoAmI() //WhoAmI is not accessible.

Но этот факт не имеет ничего общего с возможностью переопределения WhoAmI() или нет. В любом случае эта ситуация никогда не может быть, потому что Base не объявляет public WhoAmI().

Итак, в теоретическом С#, где Derived.WhoAmI() может переопределить Base.WhoAmI(), в этом нет практических преимуществ, потому что вы никогда не сможете вызывать виртуальный метод из базового класса в любом случае, поэтому опция new уже встречается ваши требования.

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

Ответ 2

Хорошо, я нашел небольшую заметку от Эрика Липперта в аннотированной ссылке на С#:

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

Итак, это преднамеренное правило, чтобы предотвратить проблему "хрупкого базового класса" и обеспечить лучшее управление версиями, то есть меньше проблем при изменении базового класса.

Но обратите внимание, что это не имеет ничего общего с безопасностью, безопасностью типа или состоянием объекта.

Ответ 3

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

Ответ 4

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

Ответ 5

Уменьшение видимости невозможно, потому что, если Base.Member был видимым и Derived.Member не был виден, это разрушило бы концепцию "Derived является Base" в ООП. Однако увеличение видимости запрещено, возможно, потому, что разработчики языка считают, что изменение видимости было бы ошибкой большую часть времени. Однако вы всегда можете использовать ключевое слово new, чтобы скрыть элементы базового класса, введя член с тем же именем, но с другим поведением. Этот новый член принадлежит к интерфейсу производных типов, поэтому, конечно, вы можете получить доступ к интерфейсу базовых типов путем отбрасывания этого базового типа. В зависимости от того, как вы пишете свой подкласс, ваш член new может эффективно увеличить видимость свойства базовых классов, но помните, что свойство базовых классов по-прежнему можно получить доступ напрямую (например, подкласс вашего подкласса может отличать this до Base и обойти ваше свойство).

Вопрос здесь в том, как и override, и new тот же именованный элемент (идентификатор) в подклассе. Это, по-видимому, невозможно. По крайней мере, я могу сказать посредством экспериментов, что public new override string foo(){return "";} не является синтаксисом для этого. Однако вы можете получить тот же эффект, используя два подкласса:

using System;
class Base
{
    protected virtual string foo()
    {
        return "Base";
    }
    public void ExhibitSubclassDependentBehavior()
    {
        Console.WriteLine("Hi, I am {0} and {1}.", GetType(), foo());
    }
}

abstract class AbstractDerived : Base
{
    protected virtual string AbstractFoo()
    {
        return base.foo();
    }
    protected override string foo()
    {
        return AbstractFoo();
    }
}

class Derived : AbstractDerived
{
    protected override string AbstractFoo()
    {
        return "Deprived";
    }
    public new string foo()
    {
        return AbstractFoo();
    }
}

static class Program
{
    public static void Main(string[] args)
    {
        var b = new Base();
        var d = new Derived();
        Base derivedAsBase = d;
        Console.Write(nameof(b) + " -> "); b.ExhibitSubclassDependentBehavior(); // "b -> Hi, I am Base and Base."
        Console.WriteLine(nameof(d) + " -> " + d.foo()); // "d -> Deprived"
        Console.Write(nameof(derivedAsBase) + " -> "); derivedAsBase.ExhibitSubclassDependentBehavior(); // "derivedAsBase -> Hi, I am Derived and Deprived."
    }
}

В промежуточном подклассе (AbstractDerived) используется override и вводится новый, иначе называемый элемент, который подкласс и подклассы могут продолжать override член базовых классов по своему усмотрению. Под-подкласс (Derived) использует new для введения нового API. Поскольку вы можете использовать только new или override с определенным идентификатором только один раз на уровень подкласса, вам необходимо два уровня подкласса, чтобы эффективно использовать оба параметра для одного и того же идентификатора.

Таким образом, вы можете изменить видимость при переопределении методов - просто боль и отсутствие синтаксиса, который я знаю, чтобы выполнить его только с одним уровнем наследования. Однако, возможно, вам придется использовать какой-то трюк, подобный этому, в зависимости от того, какие интерфейсы вы пытаетесь реализовать и как выглядит ваш базовый класс. Это может быть или не быть тем, что вы на самом деле хотите сделать. Но я все еще удивляюсь, почему С# не просто поддерживает это для начала. IOW, этот "ответ" - это просто повторное выражение вопроса OPs с обходным путем; -).

Ответ 6

Причины очевидны. Безопасность и целостность объектов.

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

Ответ 7

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

лучше задать вопрос, почему вы хотите изменить модификаторы доступа?