C # Design: Perché è nuova / esclusione richiesto su metodi astratti ma non su metodi virtuali?
-
30-09-2019 - |
Domanda
Perché è nuovo / sostituzione necessaria sui metodi astratti ma non su 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 la corrente di override membro 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.
I comprendere appieno le differenze tecniche. Tuttavia mi chiedo perché il linguaggio è stato progettato in questo modo. Non sarebbe meglio avere la stessa limitazione in "Esempio 2" come bene? Voglio dire nella maggior parte dei casi, se si crea un metodo con lo stesso nome nella classe padre, di solito l'intenzione di ignorare esso. Quindi penso affermando esplicitamente Override / Nuovo avrebbe senso su metodi virtuali.
C'è un motivo di design-saggio per questo comportamento?
Aggiornamento: Il secondo campione provoca in realtà un avvertimento. Il primo campione mostra un errore perché la sottoclasse è necessaria per implementare il metodo astratto. Non ho visto l'avvertimento in VS .. rende perfettamente senso per me ora. Grazie.
Soluzione
Con l'uso C # 3.0 del compilatore 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:
Errore CS0534: 'ConsoleApplication3.Square' non implementa ereditato membro astratto 'ConsoleApplication3.ShapesClass.Area ()'
E il seguente Attenzione per la seconda:
avviso CS0114: 'ConsoleApplication3.Square.Area ()' pelli membro ereditato 'ConsoleApplication3.ShapesClass.Area ()'. Per rendere la corrente di override membro che l'attuazione, aggiungere la parola chiave override. In caso contrario, aggiungere la nuova parola chiave.
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 si tratta di un avvertimento, perché il codice è tecnicamente corretta, ma il compilatore sospetta che non è quello che volevi dire. Questo è uno dei motivi per cui è generalmente una buona idea per permettere alle "Avvertenze trattare come gli errori di" impostazione di compilazione.
Quindi non posso repro vostro comportamento e il comportamento degli sguardi compilatore giusto per me. Quale versione del compilatore stai usando?
Altri suggerimenti
Ecco la risposta direttamente dalla C # spec.
... si nasconde un nome accessibile da un ambito ereditato provoca un avvertimento per essere segnalati. Nell'esempio
class Base
{
public void F() {}
}
class Derived: Base
{
public void F() {} // Warning, hiding an inherited name
}
la dichiarazione di F in cause derivate un avvertimento da segnalare. Nascondere un nome ereditato non è specificamente un errore, dal momento che sarebbe precludere evoluzione separata delle classi di base. Per esempio, la situazione di cui sopra potrebbe hanno avvenuto perché un secondo versione di base ha introdotto un metodo di F che non era presente in un precedente versione della classe. Aveva il sopra situazione stato un errore, allora qualsiasi modifica apportata a una classe base in un libreria di classi con versione a parte potrebbe potenzialmente causare derivato classi non sono più validi. L'avviso causata da nascondere un nome di latta ereditato essere eliminate mediante l'utilizzo del nuovo modificatore:
class Base
{
public void F() {}
}
class Derived: Base
{
new public void F() {}
}
Il nuovo modificatore indica che la F in Derivato è “nuovo”, e che è anzi destinato a nascondere il ereditata membro.
La differenza è che il metodo astratto deve essere ignorato, ma il virtuale non è così.
E 'un errore per ereditare la classe astratta (in una classe non astratta) senza implementare tutti i membri astratti, ma si ottiene solo un avviso quando eredita dalla classe senza specificare override
o new
per il metodo virtuale.
Credo che nel primo modo si ottiene l'errore del compilatore, perché il metodo astratto è nascosto e non implementato (in modo efficace la vostra classe è sbagliato, e sarebbe difficile capire perché). Nel secondo caso si ottiene solo un avvertimento, perché la classe è utilizzabile.
Versioning
A prima vista, si potrebbe pensare che il metodo nascosto non appare particolarmente utile. C'è una situazione
dove potrebbe essere necessario usarlo, però. Diciamo che si desidera utilizzare una classe denominata MotorVehicle
che
è stato scritto da un altro programmatore, e si desidera utilizzare questa classe per derivare la propria classe. Ulteriore,
Supponiamo anche che si desidera definire un metodo Accelerate()
nella classe derivata. Ad esempio:
public class Car : MotorVehicle
{
// define the Accelerate() method
public void Accelerate()
{
Console.WriteLine(“In Car Accelerate() method”);
Console.WriteLine(model + “ accelerating”);
}
}
Quindi, supponiamo che l'altro programmatore in seguito modifica la classe motoveicolo e decide
aggiungere il proprio metodo virtual Accelerate()
:
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()
dall'altro programmatore causa un problema: il
Metodo Accelerate()
nelle pelli di classe Car
il metodo Accelerate()
ereditato adesso definito nel
loro classe MotorVehicle
. Più tardi, quando si arriva a compilare il codice categoria Car
, non è chiaro al compilatore
se effettivamente destinato il metodo per nascondere il metodo ereditato. A causa di questo, il
compilatore segnala il seguente avviso quando si tenta di compilare la classe auto:
avviso CS0114: ‘Car.Accelerate ()’ pelli ereditati membro ‘MotorVehicle.Accelerate ()’. Per rendere la sostituzione degli attuali che implementazione, aggiungere la parola chiave override. In caso contrario, aggiungere la nuova parola chiave.