nova palavra-chave na assinatura do método

113

Ao realizar uma refatoração, acabei criando um método como o exemplo abaixo. O tipo de dados foi alterado para simplificar.

Eu já tive uma declaração de atribuição como esta:

MyObject myVar = new MyObject();

Ele foi reformulado para isso por acidente:

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

Isso foi resultado de um erro de recortar / colar da minha parte, mas a newpalavra - chave em private static newé válida e compila.

Pergunta : O que a newpalavra - chave significa em uma assinatura de método? Presumo que seja algo introduzido no C # 3.0?

Como isso difere override?

p.campbell
fonte
5
Algumas notas sobre a conveniência de ocultar o método: blogs.msdn.com/ericlippert/archive/2008/05/21/…
Eric Lippert
2
@Eric .. Ótima postagem. Nunca pensei em método escondido dessa forma, como em sim, o objeto É uma coisa, mas agora o estamos apresentando como outra coisa, então queremos o comportamento da coisa que o apresentamos COMO. Inteligente ...
BFree
1
Uma pergunta duplicada no futuro e tentei responder com alguns detalhes: Use nova palavra-chave em c #
Ken Kin
1
O uso de newcomo um modificador em um método (ou outro membro de tipo) não é "algo introduzido no C # 3.0". Ele está lá desde a primeira versão do C #.
Jeppe Stig Nielsen
1
Existem cerca de 4 duplicatas desta pergunta no SO. Na minha opinião, este lhe dará o melhor entendimento: stackoverflow.com/questions/3117838/… . É respondido por @EricLippert.
Raikol Amaro

Respostas:

101

Nova referência de palavra-chave do MSDN:

Referência MSDN

Aqui está um exemplo que encontrei na rede de um MVP da Microsoft que faz muito sentido: Link para o original

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

A substituição só pode ser usada em casos muito específicos. Do MSDN:

Você não pode substituir um método não virtual ou estático. O método básico substituído deve ser virtual, abstrato ou substituir.

Portanto, a palavra-chave 'nova' é necessária para permitir que você 'substitua' métodos não virtuais e estáticos.

Kelsey
fonte
4
Acabei de executar sua amostra .. ela irá substituir mesmo que você não especifique "novo", certo?
Michael Sync
2
@MichaelSync exatamente, então por que precisamos mencionar uma nova palavra-chave?
ZoomIn
2
"Embora você possa ocultar membros sem usar o novo modificador, você recebe um aviso do compilador." de acordo com o documento vinculado. Portanto, a palavra-chave deixa claro que você pretendia ocultar e o aviso não é emitido.
Jim Counts
1
-1 -> não é obrigatório - o comportamento será o mesmo. Muito confuso para um novato newsobre o que se trata, mas acho que está literalmente lá apenas para facilitar a leitura - o compilador está simplesmente avisando que quando você chama o método da classe derivada do mesmo nome que base, você não obterá o método da classe base que pode acho ... é bizarro ... puramente para facilitar a leitura?
Don Cheadle de
1
Para fins de integridade: Se as classes A e B implementarem uma interface, IMyInterfacea implementação na classe Derivada será chamada. Assim IMyInterface c = new B(), chamaremos a implementação da classe B. Se apenas uma classe implementar a interface, o método da classe que a implementa será chamado.
Nullius
61

Não, na verdade não é "novo" (desculpe o trocadilho). É basicamente usado para "esconder" um método. IE:

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

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

Se você fizer isso:

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

O método na Base é o que será chamado, NÃO o da derivada.

Mais algumas informações: http://www.akadia.com/services/dotnet_polymorphism.html

Re sua edição: No exemplo que dei, se você "substituir" em vez de usar "novo", então, quando você chamar b.Method (); o Método da classe Derivada seria chamado por causa do polimorfismo.

BFree
fonte
public class Base {public virtual void Method () {Console.WriteLine ("Base"); }} public class Derived: Base {public void Method () {Console.WriteLine ("Derived"); }} Base b = novo derivado (); b.Método (); Eu tenho "Base" .. Se eu adicionar "novo" na classe Derivado, ainda recebo "Base" ..
Michael Sync
@michael o método ainda é virtual
Rune FS
@MichaelSync: Você deve estar vendo um aviso com esse código; Aviso Derived.Method () 'oculta o membro herdado' Base.Method () '. Para fazer o membro atual substituir essa implementação, adicione a palavra-chave override. Caso contrário, adicione a nova palavra-chave.
Christopher McAtackney
2
@MichaelSync Se a palavra "substituir" for omitida, o comportamento padrão é "novo", por exemplo, ocultação de método. Portanto, o fato de você estar deixando a palavra nova de fora não faz diferença.
BFree
1
Sim. É o que eu também acho. Não sei por que C # adicionou a palavra-chave "nova" .. é apenas para fazer o aviso desaparecer .. stackoverflow.com/questions/8502661/…
Michael Sync
22

Como outros explicaram, ele é usado para ocultar um método existente. É útil para substituir um método que não seja virtual na classe pai.

Lembre-se de que criar um "novo" membro não é polimórfico. Se você converter o objeto para o tipo base, ele não usará o membro do tipo derivado.

Se você tem uma classe base:

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

E então a classe derivada:

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

}

Se você declarar um tipo de DerivedTypee depois convertê-lo, o método DoSomething()não é polimórfico, ele chamará o método da classe base, não o derivado.

BaseClass t = new DerivedType();
t.DoSomething();// Calls the "DoSomething()" method of the base class.
Dan Herbert
fonte
1
Ele chamará o método da classe base mesmo que você remova "novo" de DerievedType também.
Michael Sync
2
Acho que seu segundo parágrafo acerta todo esse tópico. Ao lidar com chamadas de uma referência de classe base , "novo" não é polimórfico ... ou seja. Você obtém exatamente o que especifica ao fazer a chamada. "override" é polimórfico ... ie. Você obtém o que a hierarquia de classes especifica.
Jonathon Reinhart
como isso é algo tão vagamente definido? Muito frustrante para um novato. E não, não tem nada a ver com polimorfismo - tem exatamente o mesmo comportamento que simplesmente não ter overrideassinatura de método
Don Cheadle
O que está oculto de quê? Certamente não pode ser que newoculte o tipo base do tipo derivado, porque você não pode sempre acessar o tipo base usando a base.<type>notação?
thatWiseGuy
@thatWiseGuy Está "oculto" no sentido de que o método base não será chamado ao usar a classe derivada em seu código. Ele ainda pode ser chamado internamente usando base.<method>e também pode ser chamado externamente se você converter para o tipo base em seu código. É tecnicamente mais preciso pensar nisso como "anulando" o método básico do que "ocultando-o".
Dan Herbert
7

Dos documentos:

Se o método na classe derivada for precedido com a nova palavra-chave, o método será definido como independente do método na classe base.

O que isso significa na prática:

Se você herda de outra classe e tem um método que compartilha a mesma assinatura, você pode defini-lo como 'novo' para que seja independente da classe pai. Isso significa que se você tiver uma referência à classe 'pai', essa implementação será executada; se você tiver uma referência à classe filha, essa implementação será executada.

Pessoalmente, tento evitar a palavra-chave 'nova', pois normalmente significa que entendi errado minha hierarquia de classes, mas há momentos em que pode ser útil. Um lugar é para controle de versão e compatibilidade com versões anteriores.

Há muitas informações no MSDN para isso .

Jonnii
fonte
O fato de que esse comportamento ocorre não tem nada a ver com newestar lá. É sintático / legível.
Don Cheadle de
3

Isso significa que o método substitui um método com o mesmo nome herdado da classe base. No seu caso, você provavelmente não tem um método com esse nome na classe base, o que significa que a nova palavra-chave é totalmente supérflua.

Robin Clowers
fonte
3

Resumindo a história - NÃO é necessário, NÃO muda NENHUM comportamento e está PURAMENTE lá para facilitar a leitura.

É por isso que no VS você verá um pouco distorcido, mas seu código será compilado e executado perfeitamente e conforme o esperado.

É preciso perguntar se realmente valeu a pena criar a newpalavra - chave quando tudo o que ela significa é o reconhecimento do desenvolvedor "Sim, eu sei que estou escondendo um método básico, sim, eu sei que não estou fazendo nada relacionado a virtualou overriden(polimorfismo) - Eu realmente quero apenas criar seu próprio método ".

É um pouco bizarro para mim, mas talvez apenas porque venho de um Javabackground e há essa diferença fundamental entre C#herança e Java: Em Java, os métodos são virtuais por padrão, a menos que sejam especificados por final. Em C#, os métodos são finais / concretos por padrão, a menos que especificado por virtual.

Don Cheadle
fonte
1

Do MSDN :

Use o novo modificador para ocultar explicitamente um membro herdado de uma classe base. Para ocultar um membro herdado, declare-o na classe derivada usando o mesmo nome e modifique-o com o novo modificador.

Doug R
fonte
0

Tenha cuidado com isso.
Você tem um método definido em uma interface que é implementada em uma classe base. Em seguida, você cria uma classe derivada que oculta o método da interface, mas não declara especificamente a classe derivada como implementando a interface. Se você chamar o método por meio de uma referência à interface, o método da classe base será chamado. No entanto, se sua classe derivada implementar especificamente a interface, seu método será chamado de qualquer tipo de referência usado.

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();

    }
}
Overfilledwaistcoat
fonte