Каковы преимущества использования базового класса для определения производного класса? [Дубликат]

Это исправленная версия https://stackoverflow.com/a/33017514/5973334

Это попытается проанализировать строку и вернуть либо int, либо float в зависимости от того, что представляет строка. Это может привести к синтаксическим ошибкам синтаксического анализа или , которые имеют некоторое неожиданное поведение .

  def get_int_or_float(v):
        number_as_float = float(v)
        number_as_int = int(number_as_float)
        return number_as_int if number_as_float == number_as_int else 
        number_as_float

72
задан Olivier Jacot-Descombes 3 June 2014 в 15:50
поделиться

8 ответов

Хорошие вопросы. Позвольте мне переформулировать их.

Почему легально скрывать метод другим методом?

Позвольте мне ответить на этот вопрос с примером , У вас есть интерфейс от CLR v1:

interface IEnumerable
{
    IEnumerator GetEnumerator();
}

Super. Теперь в CLR v2 у вас есть дженерики, и вы думаете, что «человек, если бы у нас были родословные в v1, я бы сделал это универсальным интерфейсом, но я этого не сделал. Теперь я должен сделать что-то совместимое с этим, когда , так что я получаю преимущества генериков, не теряя обратной совместимости с кодом, ожидающим IEnumerable. "

interface IEnumerable<T> : IEnumerable
{
    IEnumerator<T> .... uh oh

Что вы собираетесь называть методом GetEnumerator из IEnumerable<T>? Помните, что вы хотите скрыть GetEnumerator на базовом базовом интерфейсе. Вы never хотите, чтобы эта вещь вызывалась, если вы явно не находитесь в ситуации обратной совместимости.

Это само по себе оправдывает скрытие метода. Дополнительные мысли об оправдании метода скрытия см. В моей статье по этому вопросу .

Почему скрытие без «нового» вызывает предупреждение?

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

Почему скрывается без «нового» предупреждения, а не ошибки?

По той же причине. Возможно, вы случайно что-то скрываете, потому что только что выбрали новую версию базового класса. Это происходит все время. FooCorp создает базовый класс B. BarCorp создает производный класс D с помощью метода Bar, потому что их клиенты любят этот метод. FooCorp видит это и говорит, что это хорошая идея, мы можем поместить эту функциональность в базовый класс. Они делают это и поставляют новую версию Foo.DLL, и когда BarCorp подбирает новую версию, было бы неплохо, если бы им сказали, что их метод теперь скрывает метод базового класса.

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

Почему скрывается и не переопределяет значение по умолчанию?

Поскольку виртуальное переопределение опасно . Виртуальное переопределение позволяет производным классам изменять поведение кода, который был скомпилирован для использования базовых классов. Делать что-то опасное, как сделать переопределение, должно быть то, что вы делаете сознательно и намеренно , не случайно.

120
ответ дан Eric Lippert 28 August 2018 в 01:08
поделиться

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

Это отличается от переопределения элемента базового класса; для этого элемент базового класса должен быть помечен как абстрактный или виртуальный. Так что не каждый член базового класса может быть переопределен. Предположим, вы подклассифицируете класс из чужой сборки, и у вас нет доступа или не хотите возвращаться и модифицировать этот код, чтобы сделать участников виртуальными. Без нового ключевого слова вы бы застряли.

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

-1
ответ дан CodingGorilla 28 August 2018 в 01:08
поделиться

В C # члены запечатываются по умолчанию, что означает, что вы не можете их переопределить (если они не помечены ключевыми словами virtual или abstract) и по соображениям производительности . Новый модификатор используется, чтобы явно скрыть унаследованный элемент.

5
ответ дан Community 28 August 2018 в 01:08
поделиться

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

. Стратегия компилятора .Net - это выдавать предупреждения, если что-то может пойти неправильно, чтобы быть в безопасности, поэтому в этом случае, если переопределение было по умолчанию, должно быть предупреждение для каждого метода overriden - что-то вроде «warning: проверьте, действительно ли вы хотите переопределить».

2
ответ дан František Žiačik 28 August 2018 в 01:08
поделиться

Стоит отметить, что эффект только new в этом контексте заключается в подавлении предупреждения. В семантике нет изменений.

Итак, один ответ: нам нужно new сообщить компилятору, что скрытие намеренное и избавиться от предупреждения.

Следующий вопрос: если вы не будете / не можете переопределить метод, зачем вы вводите другой метод с тем же именем? Потому что скрытие - это, по сути, конфликт имен. И вы, конечно, избегаете этого в большинстве случаев.

Единственная веская причина, по которой я могу думать о намеренном укрытии, - это когда на вас навязывается имя интерфейса.

12
ответ дан Henk Holterman 28 August 2018 в 01:08
поделиться

Если методу в производном классе предшествует новое ключевое слово, этот метод определяется как независимый от метода в базовом классе

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

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

14
ответ дан Incognito 28 August 2018 в 01:08
поделиться

Мое предположение в основном было связано с наследованием множественного интерфейса. Используя дискретные интерфейсы, было бы очень возможно, что два разных интерфейса используют одну и ту же подпись метода. Разрешение использования ключевого слова 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
    }
}
0
ответ дан Matthew Whited 28 August 2018 в 01:08
поделиться

Как отмечено, скрытие метода / свойства позволяет изменить вещи о методе или свойстве, которые в противном случае не могут быть легко изменены. Одна из ситуаций, когда это может быть полезна, позволяет унаследованному классу иметь свойства чтения и записи, которые доступны только для чтения в базовом классе. Например, предположим, что базовый класс имеет кучу свойств только для чтения, называемых Value1-Value40 (конечно, настоящий класс будет использовать лучшие имена). Запечатанный потомок этого класса имеет конструктор, который берет объект базового класса и копирует значения оттуда; после этого класс не позволяет им изменять. Другой, наследуемый, потомок объявляет свойства чтения и записи, называемые Value1-Value40, которые при чтении ведут себя так же, как версии базового класса, но при написании позволяют записывать значения. Сетевой эффект будет заключаться в том, что код, который хочет, чтобы экземпляр базового класса, который он знает, никогда не изменится, может создать новый объект класса только для чтения, который может копировать данные из переданного объекта без необходимости беспокоиться о том, доступен только для чтения или чтения-записи.

Одно раздражение с помощью этого подхода - возможно, кто-то может мне помочь, - это то, что я не знаю, как и тень, и переопределить определенное свойство внутри тот же класс. Предоставляет ли какой-либо из языков CLR (я использую vb 2005)? Было бы полезно, если бы объект базового класса и его свойства могли быть абстрактными, но для этого требовалось бы, чтобы промежуточный класс переопределял свойства Value1 до Value40 до того, как класс потомков мог их затенять.

0
ответ дан supercat 28 August 2018 в 01:08
поделиться
Другие вопросы по тегам:

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