Используйте новое ключевое слово, если скрытие было предназначено - PullRequest
54 голосов
/ 16 января 2009

У меня есть следующий фрагмент кода, который генерирует предупреждение «Использовать новое ключевое слово, если было скрыто намерение» в VS2008:

public double Foo(double param)
{
   return base.Foo(param);
}

Функция Foo() в базовом классе защищена, и я хочу подвергнуть ее модульному тесту, поместив его в класс-обертку исключительно для целей модульного тестирования. То есть класс-обертка не будет использоваться ни для чего другого. Итак, у меня есть один вопрос: это принятая практика?

Вернуться к предупреждению new. Зачем мне в этом сценарии новая функция переопределения?

Ответы [ 4 ]

69 голосов
/ 16 января 2009

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

Разница возникает, когда ваш метод делает что-то другое; любая переменная, которая ссылается на производный класс и вызывает Foo(), будет делать что-то другое (даже с тем же объектом), чем переменная, которая ссылается на класс base и вызывает Foo():

SomeDerived obj = new SomeDerived();
obj.Foo(); // runs the new code
SomeBase objBase = obj; // still the same object
objBase.Foo(); // runs the old code

Это, очевидно, может повлиять на любой существующий код, который знает о SomeDerived и вызывает Foo(), т. Е. Теперь он работает совершенно другим методом.

Кроме того, обратите внимание, что вы можете пометить его protected internal и использовать [InternalsVisibleTo] для предоставления доступа к вашему модульному тесту (это наиболее распространенное использование [InternalsVisibleTo]; тогда ваши модульные тесты могут получить к нему доступ напрямую без производный класс.

38 голосов
/ 16 января 2009

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

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

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

8 голосов
/ 16 января 2009

Вы меняете видимость без имени. Вызовите свою функцию TestFoo, и она будет работать. Да, ИМХО это приемлемо для подкласса по этой причине.

1 голос
/ 24 июля 2014

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

Однако в последнее время я действительно нуждался в этом ключевом слове, главным образом потому, что в языке отсутствуют некоторые другие надлежащие функции синтаксиса, например, для завершения существующего средства доступа:

Если вы считаете старомодный класс вроде:

KeyedCollection<TKey, TItem>

Вы заметите, что аксессор для доступа к индексу корыта предметов:

TItem this[Int32 index] { get; set; }

Имеет оба { get; set; }, и они, конечно, обязательны из-за наследования относительно ICollection<T> и Collection<T>, но есть только один { get; } для доступа к элементам через их ключи (у меня есть некоторые предположения об этом дизайне и для этого есть множество причин, поэтому, пожалуйста, обратите внимание, что я взял KeyedCollection<TKey, TItem>) только для иллюстрации).

В любом случае, для доступа к ключам есть только один метод получения:

TItem this[TKey key] { get; }

Но что если я захочу добавить поддержку { set; }, технически говоря, это не так глупо, особенно если вы продолжаете рассуждать из прежнего определения свойства, это всего лишь метод ... единственный способ - реализовать явно другой фиктивный интерфейс, но когда вы хотите сделать неявным, вы должны придумать ключевое слово new, я скрываю определение метода доступа, сохраняя get; базовое определение и просто добавьте набор, наполненный некоторыми личными вещами, чтобы он работал.

Я думаю, что для этого очень специфического сценария это ключевое слово идеально применимо, в частности, в отношении контекста, в котором ничего не приведено к части { get; }.

  public new TItem this[TKey key]
  { 
      get { return base... }
      set { ... }
  }

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...