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

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

Я смотрел этот пост в блоге и имел следующие вопросы:

  • Для чего нам нужно ключевое слово new, просто нужно указать, что метод базового класса скрыт. Я имею в виду, зачем нам это нужно? Если мы не используем ключевое слово override, не скрываем ли мы метод базового класса?
  • Почему значение по умолчанию в С# скрывается и не переопределяется? Почему дизайнеры реализовали это так?
4b9b3361

Ответ 1

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

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

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

interface IEnumerable
{
    IEnumerator GetEnumerator();
}

Super. Теперь в 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 подбирает новую версию, было бы неплохо, если бы им сказали, что их метод теперь скрывает метод базового класса.

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

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

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

Ответ 2

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

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

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

Ответ 3

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

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

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

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

Ответ 5

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

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

Ответ 6

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

Ответ 7

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

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

Ответ 8

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

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

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