новое ключевое слово в сигнатуре метода

113

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

Ранее у меня был такой оператор присваивания:

MyObject myVar = new MyObject();

Он был изменен на это случайно:

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

Это было результатом моей ошибки вырезания / вставки, но newключевое слово in private static newдопустимо и компилируется.

Вопрос : Что означает newключевое слово в сигнатуре метода? Я полагаю, это что-то введенное в C # 3.0?

Чем это отличается от override?

p.campbell
источник
5
Некоторые примечания о желательности сокрытия метода: blogs.msdn.com/ericlippert/archive/2008/05/21/…
Эрик Липперт,
2
@Eric .. Отличный пост. Я никогда не думал о сокрытии метода таким образом, поскольку в случае «да» объект ЕСТЬ одно, но теперь мы представляем его как нечто другое, поэтому нам нужно поведение того, что мы представляем как его. Умно ...
BFree
1
Повторяющийся вопрос в будущем, и я попытался ответить на него более подробно: Используйте новое ключевое слово в C #
Кен Кин,
1
Использование newмодификатора в качестве модификатора для метода (или другого члена типа) не является «чем-то введенным в C # 3.0». Он был там с первой версии C #.
Йеппе Стиг Нильсен
1
В SO есть примерно 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

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

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

Таким образом, ключевое слово new необходимо, чтобы позволить вам «переопределить» невиртуальные и статические методы.

Kelsey
источник
4
Я просто запустил ваш образец ... он переопределит, даже если вы не укажете "новый", верно?
Майкл Синк,
2
@MichaelSync именно так, зачем нам упоминать новое ключевое слово?
ZoomIn
2
«Хотя вы можете скрыть элементы без использования модификатора new, вы получите предупреждение компилятора». согласно связанному документу. Таким образом, ключевое слово дает понять, что вы намеревались спрятаться, и предупреждение не выдается.
Джим Графс
1
-1 -> совсем не обязательно - поведение будет таким же. Это очень сбивает с толку новичка new, но я думаю, что это буквально только для удобства чтения - компилятор просто предупреждает вас, что когда вы вызываете метод производного класса с тем же именем, что и базовый, вы не получите метод базового класса, который вы можете думаю .... это странно ... чисто для удобочитаемости?
Дон Чидл,
1
Для полноты: если оба класса реализуют интерфейс, IMyInterfaceбудет вызываться реализация класса Derived. Таким образом 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();

Будет вызван метод в Base, а НЕ в производном.

Дополнительная информация: http://www.akadia.com/services/dotnet_polymorphism.html

Повторите свое редактирование: в примере, который я привел, если вы должны «переопределить» вместо использования «new», то при вызове b.Method (); Метод производного класса будет вызываться из-за полиморфизма.

BFree
источник
общедоступный класс Base {общедоступный виртуальный метод void () {Console.WriteLine ("База"); }} открытый класс Derived: Base {public void Method () {Console.WriteLine ("Derived"); }} База b = новый Derived (); b.Method (); Я получил "Базовый" .. Если я добавлю "новый" в производный класс, я все равно получу "Базовый" ..
Майкл Синк,
@michael метод все еще виртуальный
Rune FS
@MichaelSync: вы должны увидеть предупреждение с этим кодом; Предупреждение Derived.Method () 'скрывает унаследованный член' Base.Method () '. Чтобы текущий член переопределил эту реализацию, добавьте ключевое слово override. В противном случае добавьте новое ключевое слово.
Christopher McAtackney
2
@MichaelSync Если слово «переопределить» опущено, то поведение по умолчанию - «новое», например, скрытие метода. Так что тот факт, что вы оставляете слово «новое», не имеет значения.
BFree
1
Да. Я тоже так думаю. Не уверен, почему C # добавил ключевое слово «новое» ... просто для того, чтобы предупреждение исчезло ... stackoverflow.com/questions/8502661/…
Майкл Синк
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
Он вызовет метод из базового класса, даже если вы удалите "новый" из DercedType ..
Майкл Синк,
2
Я думаю, что ваш второй абзац отражает всю эту тему. При работе с вызовами из ссылки на базовый класс «новый» не полиморфен ... т.е. Вы получаете именно то, что указываете, когда звоните. "override" полиморфен ... т.е. Вы получаете то, что определяет иерархия классов .
Джонатон Рейнхарт
как это нечто такое неопределенное? Очень неприятно для новичка. И нет, это не имеет ничего общего с полиморфизмом - он ведет себя точно так же, как просто отсутствие overrideсигнатуры метода
Дон Чидл
Что от чего скрыто? Конечно, не может быть, что newбазовый тип скрывается от производного типа, потому что не всегда ли вы можете получить доступ к базовому типу, используя base.<type>нотацию?
thatWiseGuy
@thatWiseGuy Он «скрыт» в том смысле, что базовый метод не будет вызываться при использовании производного класса в вашем коде. Его по-прежнему можно вызывать изнутри base.<method>, а также можно вызывать извне, если вы приведете его к базовому типу в своем коде. Технически точнее думать об этом как о «переопределении» базового метода, чем о «сокрытии».
Дэн Герберт
7

Из документов:

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

Что это означает на практике:

Если вы наследуете от другого класса и у вас есть метод с той же сигнатурой, вы можете определить его как «новый», чтобы он не зависел от родительского класса. Это означает, что если у вас есть ссылка на «родительский» класс, тогда эта реализация будет выполнена, если у вас есть ссылка на дочерний класс, тогда эта реализация будет выполнена.

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

В MSDN есть много информации по этому поводу .

Джонни
источник
Тот факт, что такое поведение происходит, не имеет ничего общего с newприсутствием здесь. Это синтаксис / читабельность.
Дон Чидл,
3

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

Робин Клоуэрс
источник
3

Короче говоря - это НЕ требуется, это НЕ меняет поведения, и это ИСТИННО для удобства чтения.

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

Следует задаться вопросом, действительно ли стоило создавать newключевое слово, когда все, что оно означает, - это подтверждение разработчика: «Да, я знаю, что скрываю базовый метод, да, я знаю, что не делаю ничего, связанного с virtualили overriden(полиморфизмом) - Я действительно хочу создать свой собственный метод ».

Для меня это немного странно, но, возможно, только потому, что я вырос в Javaфоновом режиме и есть фундаментальное различие между C#наследованием и Java: In Java, методы являются виртуальными по умолчанию, если это не указано в final. В C#, методы по умолчанию являются окончательными / конкретными, если не указано иное virtual.

Дон Чидл
источник
1

Из MSDN :

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

Дуг Р.
источник
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();

    }
}
Жилет с набивным рисунком
источник