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 interviewinterview, pertinent snippets are below.
Keep in mind that Java has virtual by default with the finalfinal 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.
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
and override
in C#, see this MSDN pageMicrosoft Docs page).
- You created an API, which has a class called
Vehicle
.You created an API, which has a class called
Vehicle
. - I started using your API and derived
Vehicle
.I started using your API and derived
Vehicle
. - Your
Vehicle
class did not have any methodPerformEngineCheck()
.Your
Vehicle
class did not have any methodPerformEngineCheck()
. - In my
Car
class, I add a methodPerformEngineCheck()
.In my
Car
class, I add a methodPerformEngineCheck()
. - You released a new version of your API and added a
PerformEngineCheck()
.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.
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.
So when I recompile against your new API, C# warns me of this issue, e.g.
If the base
PerformEngineCheck()
was notvirtual
:app2.cs(15,17): warning CS0108: 'Car.PerformEngineCheck()' hides inherited member 'Vehicle.PerformEngineCheck()'. Use the new keyword if hiding was intended.
And if the base
PerformEngineCheck()
wasvirtual
:app2.cs(15,17): warning CS0114: 'Car.PerformEngineCheck()' hides inherited member 'Vehicle.PerformEngineCheck()'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword.
If the base PerformEngineCheck()
was not virtual
:
<!-- language: none -->
app2.cs(15,17): warning CS0108: 'Car.PerformEngineCheck()' hides inherited member 'Vehicle.PerformEngineCheck()'.
Use the new keyword if hiding was intended.
And if the base PerformEngineCheck()
was virtual
:
<!-- language: none -->
app2.cs(15,17): warning CS0114: 'Car.PerformEngineCheck()' hides inherited member 'Vehicle.PerformEngineCheck()'.
To make the current member override that implementation, add the override keyword. Otherwise add the new keyword.
- 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.
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()
.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()
.
EDIT: While going through the comments, I also came across a write-up by Eric Lippertwrite-up by Eric Lippert (then one of the members of C# design committee) on other example scenarios (mentioned by Brian).