Почему мы не можем изменять модификатор доступа при переопределении методов в С#?
В С# мы не можем изменять модификатор доступа, переопределяя метод из базового класса.
например.
Class Base
{
**protected** string foo()
{
return "Base";
}
}
Class Derived : Base
{
**public** override string foo()
{
return "Derived";
}
}
Это недействительно в С#, оно даст ошибку времени компиляции.
Я хочу знать причину, почему это не разрешено. Есть ли какая-либо техническая проблема или может привести к чему-то, что несовместимо с точки зрения ограничения доступа???
Ответы
Ответ 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
если у него были разные модификаторы доступа, вы не можете больше рассматривать его таким же способом. вид предполагает проблему с дизайном модели.
лучше задать вопрос, почему вы хотите изменить модификаторы доступа?