In Java there are no virtual
, new
, override
keywords for method definition. So the working of a method is easy to understand. Cause if DerivedClass extends BaseClass and has a method with same name and same signature of BaseClass then the overriding will take place at run-time polymorphism (provided the method is not static
).
BaseClass bcdc = new DerivedClass();
bcdc.doSomething() // will invoke DerivedClass's doSomething method.
Now come to C# there can be so much confusion and hard to understand how the new
or virtual+derive
or new + virtual override is working.
I'm not able to understand why in the world I'm going to add a method in my DerivedClass
with same name and same signature as BaseClass
and define a new behaviour but at the run-time polymorphism, the BaseClass
method will be invoked! (which is not overriding but logically it should be).
In case of virtual + override
though the logical implementation is correct, but the programmer has to think which method he should give permission to user to override at the time of coding. Which has some pro-con (let's not go there now).
So why in C# there are so much space for un-logical reasoning and confusion. So may I reframe my question as in which real world context should I think of use virtual + override
instead of new
and also use of new
instead of virtual + override
?
After some very good answers especially from Omar, I get that C# designers gave stress more about programmers should think before they make a method, which is good and handles some rookie mistakes from Java.
Now I've a question in mind. As in Java if I had a code like
Vehicle vehicle = new Car();
vehicle.accelerate();
and later I make new class SpaceShip
derived from Vehicle
. Then I want to change all car
to a SpaceShip
object I just have to change single line of code
Vehicle vehicle = new SpaceShip();
vehicle.accelerate();
This will not break any of my logic at any point of code.
But in case of C# if SpaceShip
does not override the Vehicle
class' accelerate
and use new
then the logic of my code will be broken. Isn't that a disadvantage?
Best Answer
Since you asked why C# did it this way, it's best to ask the C# creators. Anders Hejlsberg, the lead architect for C#, answered why they chose not to go with virtual by default (as in Java) in an interview, pertinent snippets are below.
Keep in mind that Java has virtual by default with the final keyword to mark a method as non-virtual. Still two concepts to learn, but many folks do not know about the final keyword or don't use it proactively. C# forces one to use virtual and new/override to consciously make those decisions.
The interview has more discussion about how developers think about class inheritance design, and how that led to their decision.
Now to the following question:
This would be when a derived class wants to declare that it does not abide by the contract of the base class, but has a method with the same name. (For anyone who doesn't know the difference between
new
andoverride
in C#, see this Microsoft Docs page).A very practical scenario is this:
You created an API, which has a class called
Vehicle
.I started using your API and derived
Vehicle
.Your
Vehicle
class did not have any methodPerformEngineCheck()
.In my
Car
class, I add a methodPerformEngineCheck()
.You released a new version of your API and added a
PerformEngineCheck()
.I cannot rename my method because my clients are dependent on my API, and it would break them.
So when I recompile against your new API, C# warns me of this issue, e.g.
If the base
PerformEngineCheck()
was notvirtual
:And if the base
PerformEngineCheck()
wasvirtual
:Now, I must explicitly make a decision whether my class is actually extending the base class' contract, or if it is a different contract but happens to be the same name.
By making it
new
, I do not break my clients if the functionality of the base method was different from the derived method. Any code that referencedVehicle
will not seeCar.PerformEngineCheck()
called, but code that had a reference toCar
will continue to see the same functionality that I had offered inPerformEngineCheck()
.A similar example is when another method in the base class might be calling
PerformEngineCheck()
(esp. in the newer version), how does one prevent it from calling thePerformEngineCheck()
of the derived class? In Java, that decision would rest with the base class, but it does not know anything about the derived class. In C#, that decision rests both on the base class (via thevirtual
keyword), and on the derived class (via thenew
andoverride
keywords).Of course, the errors that the compiler throws also provide a useful tool for the programmers to not unexpectedly make errors (i.e. either override or provide new functionality without realizing so.)
Like Anders said, real world forces us into such issues which, if we were to start from scratch, we would never want to get into.
EDIT: Added an example of where
new
would have to be used for ensuring interface compatibility.EDIT: While going through the comments, I also came across a write-up by Eric Lippert (then one of the members of C# design committee) on other example scenarios (mentioned by Brian).
PART 2: Based on updated question
Who decides whether
SpaceShip
is actually overriding theVehicle.accelerate()
or if it's different? It has to be theSpaceShip
developer. So ifSpaceShip
developer decides that they are not keeping the contract of the base class, then your call toVehicle.accelerate()
should not go toSpaceShip.accelerate()
, or should it? That is when they will mark it asnew
. However, if they decide that it does indeed keep the contract, then they will in fact mark itoverride
. In either case, your code will behave correctly by calling the correct method based on the contract. How can your code decide whetherSpaceShip.accelerate()
is actually overridingVehicle.accelerate()
or if it is a name collision? (See my example above).However, in the case of implicit inheritance, even if
SpaceShip.accelerate()
did not keep the contract ofVehicle.accelerate()
, the method call would still go toSpaceShip.accelerate()
.