2010-09-03 17 views
13

Perché è necessario/override obbligatorio sui metodi astratti ma non sui metodi virtuali?Design C#: perché è necessario/override obbligatorio sui metodi astratti ma non sui metodi virtuali?

Esempio 1:

abstract class ShapesClass 
{ 
    abstract public int Area(); // abstract! 
} 

class Square : ShapesClass 
{ 
    int x, y; 

    public int Area() // Error: missing 'override' or 'new' 
    { 
     return x * y; 
    } 
} 

Il compilatore mostrerà questo errore: Per rendere il membro corrente ignorare che l'attuazione, aggiungere la parola chiave override. In caso contrario, aggiungere la nuova parola chiave

Esempio 2:

class ShapesClass 
{ 
    virtual public int Area() { return 0; } // it is virtual now! 
} 

class Square : ShapesClass 
{ 
    int x, y; 

    public int Area() // no explicit 'override' or 'new' required 
    { 
     return x * y; 
    } 
} 

Questo compilerà bene, nascondendo il metodo di default.

Capisco perfettamente le differenze tecniche. Tuttavia mi chiedo perché il linguaggio sia stato progettato in quel modo. Non sarebbe meglio avere la stessa restrizione anche nel "Campione 2"? Voglio dire nella maggior parte dei casi se crei un metodo con lo stesso nome della classe genitore, di solito intendi sovrascriverlo. Quindi penso che affermare esplicitamente Override/New avrebbe senso anche sui metodi virtuali.

Esiste un motivo di progettazione per questo comportamento?

Aggiornamento: Il secondo campione causa effettivamente un avviso. Il primo esempio mostra un errore perché la sottoclasse è necessaria per implementare il metodo astratto. Non ho visto l'avviso in VS .. ha perfettamente senso per me ora. Grazie.

+0

Se hai ragione (non hanno avuto il tempo di controllare) allora sembra un bug nel compilatore; Mi aspetterei che avvertisse in entrambi i casi. Quale versione del compilatore stai usando? –

+0

Non sono sicuro del motivo per cui non si ottiene un errore, ma è necessario eseguire l'override o una nuova IS su un override virtuale e l'esempio sopra riportato NON viene compilato. –

+0

Viene visualizzato un avviso: "Avviso 'StackOverflow_Test.Square.Area()' nasconde il membro ereditato 'Stackoverflow_Test.ShapesClass.Area()'. Per fare in modo che il membro corrente sostituisca tale implementazione, aggiungere la parola chiave override. la nuova parola chiave. " –

risposta

11

Utilizzando sia il compilatore C# 3.0 come spedito in .NET 3.5 SP1, o il compilatore C# 4.0 come spedito in .NET 4.0, ottengo il seguente errore per il primo esempio:

error CS0534: 'ConsoleApplication3.Square' does not implement inherited abstract member 'ConsoleApplication3.ShapesClass.Area()'

E il seguente avvertimento per il secondo:

warning CS0114: 'ConsoleApplication3.Square.Area()' hides inherited member 'ConsoleApplication3.ShapesClass.Area()'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword.

Nel primo caso si tratta di un errore perché non sono in realtà l'override del metodo di base, il che significa che non v'è alcuna implementazione per il metodo astratto in una classe concreta. Nel secondo caso è un avvertimento perché il codice è tecnicamente corretto, ma il compilatore sospetta che non è quello che intendevi. Questo è uno dei motivi per cui è generalmente una buona idea abilitare l'impostazione di compilazione "trattare gli avvertimenti come errori".

Quindi non posso riprodurre il tuo comportamento e il comportamento del compilatore mi sembra giusto. Quale versione del compilatore stai usando?

+0

Ha assolutamente senso, grazie. Sembra che abbia nascosto gli avvertimenti in questo progetto, quindi non ho visto questo comportamento. – driAn

3

La differenza è che il metodo astratto deve essere sovrascritto, ma il virtuale no.

È un errore ereditare la classe astratta (in una classe non astratta) senza implementare tutti i membri astratti, ma si ottiene un avviso solo quando si eredita dalla classe senza specificare override o new per il metodo virtuale.

0

Penso che nel primo caso si ottiene l'errore del compilatore, perché il metodo astratto è nascosto e non implementato (in modo efficace la classe è sbagliata, e sarebbe difficile capire perché). Nel secondo caso ricevi solo un avvertimento, perché la classe è utilizzabile.

7

Ecco la risposta direttamente dalla specifica C#.

...hiding an accessible name from an inherited scope causes a warning to be reported. In the example

class Base 
{ 
    public void F() {} 
} 
class Derived: Base 
{ 
    public void F() {}  // Warning, hiding an inherited name 
} 

the declaration of F in Derived causes a warning to be reported. Hiding an inherited name is specifically not an error, since that would preclude separate evolution of base classes. For example, the above situation might have come about because a later version of Base introduced an F method that wasn’t present in an earlier version of the class. Had the above situation been an error, then any change made to a base class in a separately versioned class library could potentially cause derived classes to become invalid. The warning caused by hiding an inherited name can be eliminated through use of the new modifier:

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

The new modifier indicates that the F in Derived is “new”, and that it is indeed intended to hide the inherited member.

0

Versioning

A prima vista, si potrebbe pensare che il metodo nascosto non appare particolarmente utile. C'è una situazione in cui potrebbe essere necessario usarlo, tuttavia. Supponiamo che tu voglia utilizzare una classe denominata MotorVehicle che sia stata scritta da un altro programmatore e che tu voglia utilizzare questa classe per derivare la tua classe. Inoltre, supponiamo anche che tu voglia definire un metodo Accelerate() nella tua classe derivata. Per esempio:

public class Car : MotorVehicle 
{ 
    // define the Accelerate() method 
    public void Accelerate() 
    { 
     Console.WriteLine(“In Car Accelerate() method”); 
     Console.WriteLine(model + “ accelerating”); 
    } 
} 

Avanti, supponiamo che l'altro programmatore in seguito modifica la classe di autoveicolo e decide per aggiungere la propria virtual Accelerate() metodo:

public class MotorVehicle 
{ 
    // define the Accelerate() method 
    public virtual void Accelerate() 
    { 
     Console.WriteLine(“In MotorVehicle Accelerate() method”); 
     Console.WriteLine(model + “ accelerating”); 
    } 
} 

L'aggiunta di questo metodo Accelerate() dagli altri programmatori causa un problema: il metodo Accelerate() nella classe Car nasconde il metodo ereditato Accelerate() ora definito in loro classe MotorVehicle. Successivamente, quando si arriva alla compilazione della classe Car, non è chiaro al compilatore se si è intenzionalmente intenzionato a nascondere il metodo ereditato. A causa di questo, il compilatore riporta il seguente avviso quando si tenta di compilare la classe auto:

warning CS0114: ‘Car.Accelerate()’ hides inherited member ‘MotorVehicle.Accelerate()’. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword.