Я просматривал это сообщение в блоге и у меня возникли следующие вопросы:
- Зачем нам нужно
new
ключевое слово, просто чтобы указать, что метод базового класса скрывается. Я имею ввиду, зачем нам это нужно? Если мы не используемoverride
ключевое слово, разве мы не скрываем метод базового класса? - Почему в C # по умолчанию используется скрытие, а не переопределение? Почему дизайнеры реализовали это именно так?
c#
overriding
Песочница
источник
источник
Ответы:
Хорошие вопросы. Позвольте мне их переформулировать.
Позвольте мне ответить на этот вопрос примером. У вас есть интерфейс из CLR v1:
interface IEnumerable { IEnumerator GetEnumerator(); }
Супер. Теперь в CLR v2 у вас есть дженерики, и вы думаете: «Блин, если бы у нас были дженерики в v1, я бы сделал его универсальным интерфейсом. Но я этого не сделал. Я должен сделать что-то совместимое с ним сейчас, что является универсальным, чтобы Я получаю преимущества универсальных шаблонов без потери обратной совместимости с кодом, ожидающим IEnumerable ».
interface IEnumerable<T> : IEnumerable { IEnumerator<T> .... uh oh
Для чего вы собираетесь вызывать метод GetEnumerator
IEnumerable<T>
? Помните, что вы хотите, чтобы GetEnumerator был скрыт в неуниверсальном базовом интерфейсе. Вы никогда не захотите, чтобы эта вещь вызывалась, если вы явно не находитесь в ситуации обратной совместимости.Одно это оправдывает сокрытие метода. Дополнительные мысли об обосновании скрытия метода см. В моей статье на эту тему .
Потому что мы хотим обратить ваше внимание на то, что вы что-то скрываете и, возможно, делаете это случайно. Помните, что вы могли случайно что-то скрыть из-за изменения базового класса, сделанного кем-то другим, а не в результате редактирования производного класса.
Той же причине. Возможно, вы случайно что-то скрываете, потому что только что приобрели новую версию базового класса. Это происходит все время. FooCorp создает базовый класс B. BarCorp создает производный класс D с методом Bar, потому что их клиентам нравится этот метод. FooCorp видит это и говорит: «Эй, это хорошая идея, мы можем добавить эту функциональность в базовый класс». Они поступают так и выпускают новую версию Foo.DLL, и когда BarCorp подберет новую версию, было бы неплохо, если бы им сказали, что их метод теперь скрывает метод базового класса.
Мы хотим, чтобы эта ситуация была предупреждением, а не ошибкой, потому что создание ошибки означает, что это еще одна форма проблемы хрупкого базового класса . C # был тщательно разработан, чтобы при изменении базового класса влияние на код, использующий производный класс, сводилось к минимуму.
Потому что виртуальное переопределение опасно . Виртуальное переопределение позволяет производным классам изменять поведение кода, который был скомпилирован для использования базовых классов. Совершать что-то опасное, например, отменять, следует делать сознательно и намеренно , а не случайно.
источник
Если методу в производном классе предшествует ключевое слово new, метод определяется как независимый от метода в базовом классе.
Однако, если вы не укажете ни new, ни переопределения, результат будет таким же, как если бы вы указали new, но вы получите предупреждение компилятора (поскольку вы можете не знать, что скрываете метод в методе базового класса, или, возможно, вы хотели его переопределить и просто забыли включить ключевое слово).
Таким образом, это помогает вам избежать ошибок и явно показать, что вы хотите сделать, и делает код более читаемым, чтобы вы могли легко понять ваш код.
источник
Стоит отметить, что единственный эффект
new
в этом контексте - подавление предупреждения. В семантике нет изменений.Итак, один ответ: нам нужно
new
сообщить компилятору, что скрытие является преднамеренным, и избавиться от предупреждения.Следующий вопрос: если вы не хотите / не можете переопределить метод, зачем вам вводить другой метод с тем же именем? Потому что сокрытие - это, по сути, конфликт имен. И вы, конечно, в большинстве случаев этого избегаете.
Единственная веская причина, о которой я могу думать для намеренного сокрытия, - это когда имя навязывается вам интерфейсом.
источник
В C # члены по умолчанию запечатаны, что означает, что вы не можете переопределить их (если они не отмечены ключевыми словами
virtual
илиabstract
), и это по причинам производительности . Новый модификатор используется для явного скрыть унаследованный элемент.источник
Если переопределение было по умолчанию без указания
override
ключевого слова, вы могли случайно переопределить какой-либо метод своей базы только из-за равенства имен.Стратегия компилятора .Net состоит в том, чтобы на всякий случай выдавать предупреждения, если что-то может пойти не так, поэтому в этом случае, если переопределение было по умолчанию, должно быть предупреждение для каждого метода переопределения - что-то вроде `` предупреждение: проверьте, действительно ли вы хотите переопределить'.
источник
Я предполагаю, что в основном это связано с наследованием множественных интерфейсов. При использовании дискретных интерфейсов очень возможно, что два разных интерфейса будут использовать одну и ту же сигнатуру метода. Разрешение использования
new
ключевого слова позволит вам создавать эти разные реализации с одним классом, вместо того, чтобы создавать два разных класса.Обновлено ... Эрик дал мне идею, как улучшить этот пример.
public interface IAction1 { int DoWork(); } public interface IAction2 { string DoWork(); } public class MyBase : IAction1 { public int DoWork() { return 0; } } public class MyClass : MyBase, IAction2 { public new string DoWork() { return "Hi"; } } class Program { static void Main(string[] args) { var myClass = new MyClass(); var ret0 = myClass.DoWork(); //Hi var ret1 = ((IAction1)myClass).DoWork(); //0 var ret2 = ((IAction2)myClass).DoWork(); //Hi var ret3 = ((MyBase)myClass).DoWork(); //0 var ret4 = ((MyClass)myClass).DoWork(); //Hi } }
источник
new
Ключевое слово позволяет также изменить базу наследования для всех детей от этой точки.Как уже отмечалось, скрытие метода / свойства позволяет изменить некоторые аспекты метода или свойства, которые иначе нельзя было бы легко изменить. Одна из ситуаций, когда это может быть полезно, - это разрешение унаследованному классу иметь свойства чтения и записи, которые доступны только для чтения в базовом классе. Например, предположим, что базовый класс имеет набор свойств только для чтения, называемых Value1-Value40 (конечно, настоящий класс будет использовать более удачные имена). Запечатанный потомок этого класса имеет конструктор, который принимает объект базового класса и копирует оттуда значения; после этого класс не позволяет их изменять. Другой наследуемый потомок объявляет свойства чтения-записи с именем Value1-Value40, которые при чтении ведут себя так же, как версии базового класса, но при записи позволяют записывать значения.
Одно раздражение в этом подходе - возможно, кто-то может мне помочь - заключается в том, что я не знаю способа как затенять, так и переопределять конкретное свойство в одном классе. Допускает ли это какой-либо из языков CLR (я использую vb 2005)? Было бы полезно, если бы объект базового класса и его свойства могли быть абстрактными, но это потребовало бы, чтобы промежуточный класс переопределил свойства Value1 до Value40, прежде чем класс-потомок сможет их затенять.
источник