нове ключове слово в підписі методу


113

Виконуючи рефакторинг, я закінчив створити такий метод, як приклад нижче. Тип даних було змінено заради простоти.

Я попередньо мав заяву про призначення, як це:

MyObject myVar = new MyObject();

Це було відновлено випадково:

private static new MyObject CreateSomething()
{
  return new MyObject{"Something New"};
}

Це було результатом помилки вирізання / вставки з мого боку, але newключове слово в private static newдійсній і компілюється.

Питання : Що означає newключове слово у підписі методу? Я припускаю, що це щось введене в C # 3.0?

Чим це відрізняється від override?


5
Деякі зауваження щодо бажаності приховування методу: blogs.msdn.com/ericlippert/archive/2008/05/21/…
Ерік Ліпперт

2
@Eric .. Чудовий пост. Я ніколи не думав про приховування методу таким чином, як, наприклад, так, об'єкт - це одне, але ми зараз представляємо його як щось інше, тому ми хочемо, щоб поведінка тієї речі, яку ми представляємо її як. Розумний ...
BFree

1
Повторне запитання в майбутньому, і я спробував відповісти детально: Використовуйте нове ключове слово в c #
Кен Кін

1
Використання newмодифікатора aa для методу (або іншого члена типу) не є "чимось введеним у C # 3.0". Він існує ще з першої версії C #.
Jeppe Stig Nielsen

1
Ось 4 дублікату цього питання в ТА. На мою думку, саме це дасть вам найкраще розуміння: stackoverflow.com/questions/3117838/… . На нього відповідає @EricLippert.
Райкол Амаро

Відповіді:


101

Нова довідка про ключові слова від MSDN:

Довідник MSDN

Ось приклад, який я знайшов у мережі від Microsoft MVP, який мав сенс: Посилання на оригінал

public class A
{
   public virtual void One();
   public void Two();
}

public class B : A
{
   public override void One();
   public new void Two();
}

B b = new B();
A a = b as A;

a.One(); // Calls implementation in B
a.Two(); // Calls implementation in A
b.One(); // Calls implementation in B
b.Two(); // Calls implementation in B

Override може використовуватися лише в дуже конкретних випадках. Від MSDN:

Ви не можете замінити невіртуальний або статичний метод. Метод, що переосмислюється, повинен бути віртуальним, абстрактним або переопределеним

Отже, "нове" ключове слово необхідне, щоб ви могли "переоцінити" невіртуальні та статичні методи.


4
я просто запускаю ваш зразок .. він буде заміняти навіть ви не вказали "новий", правда?
Michael Sync

2
@MichaelSync точно, тож чому нам потрібно згадувати нове ключове слово?
ZoomIn

2
"Хоча ви можете приховати членів без використання нового модифікатора, ви отримуєте попередження компілятора." за пов'язаний документ Отже, за ключовим словом з’ясовується, що ви приховували приховування, і попередження не видається.
Джим рахує

1
-1 -> це зовсім не потрібно - поведінка буде однаковою. Що дуже непомітно для новачків, про що newйдеться, але я думаю, що це буквально є лише для читабельності - компілятор просто попереджає вас про те, що, коли ви викликаєте похідний метод класу з такою ж назвою як базовий, ви не отримаєте методу базового класу, який ви можете подумайте .... це химерно ... суто для читабельності?
Дон Чіддл

1
Для повноти: Якщо і A, і B клас реалізують інтерфейс, IMyInterfaceбуде викликана реалізація для класу Похідне. Таким чином IMyInterface c = new B()буде називатися реалізація B класу. Якщо лише один клас реалізує інтерфейс, буде викликаний метод із класу, який його реалізує.
Нуллій

61

Ні, насправді це не "нове" (вибачте за каламбур). В основному він використовується для "приховування" методу. IE:

public class Base
{
   public virtual void Method(){}
}

public class Derived : Base
{
   public new void Method(){}
}

Якщо ви це зробите:

Base b = new Derived();
b.Method();

Метод в Базі - це той, який буде називатися, а не той у похідному.

Більше інформації: http://www.akadia.com/services/dotnet_polymorphism.html

Перегляньте свою редагування: у прикладі, який я наводив, якщо ви переймете "замість" замість "нового", тоді, коли ви викликаєте b.Method (); Метод похідного класу буде називатися через поліморфізм.


public class Base {public virtual void Method () {Console.WriteLine ("Base"); }} публічний клас Похідне: Base {public void Method () {Console.WriteLine ("Похідне"); }} База b = нове Похідне (); б.Метод (); Я отримав "Base" .. Якщо додати "new" у клас "Отриманий", я все одно отримаю "Base" ..
Michael Sync

@michael метод досі віртуальний
Rune FS

@MichaelSync: Вам слід побачити попередження з цим кодом; Попередження Derived.Method () 'приховує спадковий член' Base.Method () '. Щоб поточний член замінив цю реалізацію, додайте ключове слово override. В іншому випадку додайте нове ключове слово.
Крістофер МакАктні

2
@MichaelSync Якщо слово "переосмислити" залишено, то поведінка за замовчуванням є "новою", наприклад, приховування методу. Тож факт, що ви залишаєте слово new out, не має значення.
BFree

1
Так. Це я теж думаю. Не впевнений, чому C # додав "нове" ключове слово. Це лише для того, щоб попередження пропало ... stackoverflow.com/questions/8502661/…
Michael Sync

22

Як пояснили інші, він використовується для приховування існуючого методу. Це корисно для зміни способу, який не є віртуальним у батьківському класі.

Майте на увазі, що створення "нового" члена не є поліморфним. Якщо ви переведете об'єкт на базовий тип, він не використовуватиме похідний член типу.

Якщо у вас базовий клас:

public class BaseClass
{
    public void DoSomething() { }
}

А потім похідний клас:

public class DerivedType : BaseClass
{
    public new void DoSomething() {}

}

Якщо ви оголосите тип, DerivedTypeа потім викладете його, метод DoSomething()не є поліморфним, він викличе метод базового класу, а не похідний.

BaseClass t = new DerivedType();
t.DoSomething();// Calls the "DoSomething()" method of the base class.

1
Він викличе метод із базового класу, навіть якщо ви видалите "нове" з DerievedType також ..
Michael Sync

2
Я думаю, ваш другий абзац висвітлює всю цю тему. При роботі з дзвінками з базового класу посилання "нове" не є поліморфним ... тобто. Ви отримуєте саме те , що ви вказати , коли ви робите виклик. "переоцінка" є поліморфною ... тобто. Ви отримуєте те, що вказує ієрархія класів .
Джонатан Райнхарт

як це щось таке невиразно визначене? Дуже засмучує новачка. І ні, це не має нічого спільного з поліморфізмом - він має таку саму поведінку, як просто не маючи overrideпідпис у методі
Дон Чейдл,

Що приховано від чого? Це, безумовно, не може newприховувати тип бази від похідного типу, тому що ви не завжди можете отримати доступ до базового типу, використовуючи base.<type>позначення?
thatWiseGuy

@thatWiseGuy Це "приховано" в тому сенсі, що базовий метод не буде викликаний при використанні похідного класу у вашому коді. Це все ще можна викликати внутрішньо, використовуючи base.<method>, а також можна викликати зовнішнє, якщо ви перейдете до базового типу у своєму коді. Технічніше точніше думати про це як про "основний" метод, ніж про "приховування" його.
Ден Герберт

7

З документів:

Якщо методу у похідному класі передує нове ключове слово, метод визначається як незалежний від методу в базовому класі.

Що це означає на практиці:

Якщо ви успадковуєте інший клас і у вас є метод, який має однаковий підпис, ви можете визначити його як "новий", щоб він не залежав від батьківського класу. Це означає, що якщо у вас є посилання на клас "батьків", то ця реалізація буде виконана, якщо у вас є посилання на дочірній клас, то ця реалізація буде виконана.

Особисто я намагаюся уникати «нового» ключового слова, оскільки це зазвичай означає, що я неправильно ієрархію свого класу, але бувають випадки, коли це може бути корисним. Одне місце - це сумісність версій та зворотна версія.

Для цього є багато інформації в MSDN .


Те, що така поведінка відбувається, не має нічого спільного з newтим, щоб бути там. Це синтаксична / читабельність.
Don Cheadle

3

Це означає, що метод замінює метод на те саме ім’я, успадкований базовим класом. У вашому випадку ви, мабуть, не маєте методу з таким іменем у базовому класі, тобто нове ключове слово абсолютно зайве.


3

Довга коротка історія - це НЕ потрібно, це змінює НЕ поведінку, і ЦІЛЬНО там для читабельності.

Ось чому в VS ви побачите трохи хитро, але ваш код буде компілюватися і працювати нормально і як очікується.

Потрібно задуматися, чи дійсно варто створити newключове слово, коли все це означає - це визнання розробника "Так, я знаю, що я приховую базовий метод. Так, я знаю, що я не роблю нічого, що стосується virtualабо overriden(поліморфізму) - Я дуже хочу просто створити це власний метод ".

Для мене це трохи химерно, але, можливо, тільки тому, що я походжу з Javaфонових даних і є ця принципова різниця між C#успадкуванням і Java: В Java, методи за замовчуванням є віртуальними, якщо не вказано final. В C#, методи є кінцевими / конкретними за замовчуванням, якщо не вказано virtual.


1

Від MSDN :

Використовуйте новий модифікатор, щоб явно приховати член, успадкований від базового класу. Щоб приховати спадковий член, оголосити його у похідному класі, використовуючи те саме ім'я, та змінити його за допомогою нового модифікатора.


0

Будьте уважні до цієї ґатчі.
У вас є метод, визначений в інтерфейсі, який реалізований в базовому класі. Потім ви створюєте похідний клас, який приховує метод інтерфейсу, але конкретно не оголошує похідний клас як реалізацію інтерфейсу. Якщо потім виклик методу через посилання на інтерфейс, викличеться метод базового класу. Однак якщо ваш похідний клас спеціально реалізує інтерфейс, його метод буде називатися залежно від типу посилання.

interface IMethodToHide
{
    string MethodToHide();
}

class BaseWithMethodToHide : IMethodToHide
{
    public string MethodToHide()
    {
        return "BaseWithMethodToHide";
    }
}

class DerivedNotImplementingInterface   : BaseWithMethodToHide
{
    new public string MethodToHide()
    {
        return "DerivedNotImplementingInterface";
    }
}

class DerivedImplementingInterface : BaseWithMethodToHide, IMethodToHide
{
    new public string MethodToHide()
    {
        return "DerivedImplementingInterface";
    }
}

class Program
{
    static void Main()
    {
        var oNoI = new DerivedNotImplementingInterface();
        IMethodToHide ioNoI = new DerivedNotImplementingInterface();

        Console.WriteLine("reference to the object type DerivedNotImplementingInterface calls the method in the class " 
            + oNoI.MethodToHide());
        // calls DerivedNotImplementingInterface.MethodToHide()
        Console.WriteLine("reference to a DerivedNotImplementingInterface object via the interfce IMethodToHide calls the method in the class " 
            + ioNoI.MethodToHide());
        // calls BaseWithMethodToHide.MethodToHide()
        Console.ReadLine();

        var oI = new DerivedImplementingInterface();
        IMethodToHide ioI = new DerivedImplementingInterface();

        Console.WriteLine("reference to the object type DerivedImplementingInterface calls the method in the class " 
            + oI.MethodToHide());
        // calls DerivedImplementingInterface.MethodToHide()
        Console.WriteLine("reference to a DerivedImplementingInterface object via the interfce IMethodToHide calls the method in the class " 
            + ioI.MethodToHide());
        // calls DerivedImplementingInterface.MethodToHide()
        Console.ReadLine();

    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.