1. Introduction
If you just broke a sweat when you saw the word overriding, don't worry — it's not scary, it's actually super handy! Method overriding is the ability to swap out the behavior of a base class method with your own in a derived class. Thanks to this, our code gets flexible, extensible, and ready for real life, where every animal definitely doesn't want to be just "some sound".
To allow a method to be overridden, the base class marks it with the virtual keyword. The derived class, to replace the implementation, uses the override keyword.
Example
public class Animal
{
public string Name { get; set; }
public virtual void MakeSound()
{
Console.WriteLine("Some generic animal sound...");
}
}
And now in the dog class:
public class Dog : Animal
{
public override void MakeSound()
{
Console.WriteLine("Woof-woof!");
}
}
- In the base class — virtual.
- In the derived class — override.
- The method signature (name, return type, parameters) must match.
Visual diagram
2. Demo: How it works
Let's see how overriding works in practice. We'll create some animals and check their sounds:
Animal pet1 = new Animal { Name = "Nameless animal" };
Dog pet2 = new Dog { Name = "Barbos" };
pet1.MakeSound(); // Outputs: Some generic animal sound...
pet2.MakeSound(); // Outputs: Woof-woof!
Now let's make it trickier:
What if we store a dog in a variable of type Animal?
Animal pet3 = new Dog { Name = "Sharik" };
pet3.MakeSound(); // ???
What do you think will happen?
Answer: It will print "Woof-woof!"
Because even though the variable is of type Animal, it "points" to a dog, so the overridden version of the method gets called!
That's the magic of dynamic (or late) binding.
3. Using the base keyword when overriding
Sometimes you don't want to completely replace the method's implementation, but extend it — like, add your own thing, then still do the old behavior. For that, you use the base keyword. It lets you call the method version from the base class.
public class Cat : Animal
{
public override void MakeSound()
{
base.MakeSound(); // call base implementation
Console.WriteLine("Meow!");
}
}
When you call this method, first it'll print "Some generic animal sound...", then "Meow!"
4. How method selection works when overriding
To really get what's happening "under the hood", imagine a table like this (virtual dispatch):
| Variable type | Object type | Which method gets called |
|---|---|---|
| Animal | Animal | Animal.MakeSound |
| Animal | Dog | Dog.MakeSound |
| Animal | Labrador | Labrador.MakeSound |
| Dog | Labrador | Labrador.MakeSound |
| Dog | Dog | Dog.MakeSound |
Main rule:
The variable type only matters for the compiler, but at runtime, the actual object type (what we created with new) is what counts.
This mechanism is called dynamic (or late) binding — that's what makes polymorphism work (more on that in the next lecture!).
Why override methods
- In GUI frameworks: you have a base window class, and you override methods to draw specific elements.
- In game engines: base class Enemy, and inherited classes implement different behaviors.
- In unit tests: you can create "stubs" (stubs, mocks) for methods.
Modern .NET frameworks use this mechanism a lot for events, template code, configuration inheritance, and even for object serialization (like with virtual properties).
5. The new keyword for hiding methods
We've already figured out that to override methods you need the virtual/override duo. But C# has another modifier related to methods in inheritance hierarchies — that's new.
Why do we need new?
new is used if in a derived class you declare a method with the same signature as in the base class, BUT you don't want to override the virtual method, you want to hide (mask) the base method.
- This is not overriding, it's hiding.
- Such a method is called by the variable type, not by the actual object type (no dynamic polymorphism!).
- The compiler will warn you if you "accidentally" hide a method without the new keyword.
Example: difference between override and new
public class Animal
{
public virtual void MakeSound()
{
Console.WriteLine("Animal makes some sound...");
}
}
public class Dog : Animal
{
// Hiding the base class method (NOT overriding)
public new void MakeSound()
{
Console.WriteLine("This is not override! Just a dog method.");
}
}
Now let's see the behavior:
Animal a = new Dog();
Dog d = new Dog();
a.MakeSound(); // "Animal makes some sound..."
d.MakeSound(); // "This is not override! Just a dog method."
- If the variable is of type Dog — the method from Dog gets called.
- If the variable is of type Animal, even if it holds a Dog — the Animal method gets called!
6. Feedback and implementation quirks
When you're just starting out with programming, it's super common to get confused when a method is "overridden" but for some reason works the old way. Usually the reason is simple: the base class doesn't have virtual, or in the derived class the method is declared with new instead of override. The second case is especially sneaky — if you call the method through a base type variable, the base version gets called, not the overridden one. So always make sure to use the right keywords.
Besides syntax errors, sometimes beginners try to change the return type when overriding. For example, make the base function return object, and in the derived one — string. You can't do that: the method signature must match exactly.
Comparison table: override vs new
| Feature | override | new |
|---|---|---|
| Mechanism | Overrides a virtual method | Hides a base class method |
| Late binding | Yes — works via dynamic polymorphism | No — works by variable type |
| Requires base to have... | virtual, abstract or already override | No |
| Recommended to use | Yes, almost always | Only in exceptional cases |
7. How overridden methods work with hierarchies
This whole overriding thing gets especially interesting if you have long inheritance chains:
public class Animal
{
public virtual void MakeSound()
{
Console.WriteLine("Animal does something...");
}
}
public class Dog : Animal
{
public override void MakeSound()
{
Console.WriteLine("Woof-woof!");
}
}
public class Labrador : Dog
{
public override void MakeSound()
{
Console.WriteLine("I'm a labrador: wow-wow!");
}
}
What happens if we write:
Animal pet = new Labrador();
pet.MakeSound(); // => "I'm a labrador: wow-wow!"
C# will always pick the "deepest" virtual method implementation that's on the actual object.
8. Common mistakes when overriding methods
The world isn't perfect, and students (and experienced devs!) sometimes make mistakes. Let's learn to dodge the most popular "gotchas" right away:
1. Forgot virtual in the base class
public class Animal
{
public void MakeSound() { ... } // No 'virtual'
}
public class Dog : Animal
{
// Compile error! Can't override.
public override void MakeSound()
{
Console.WriteLine("Woof!");
}
}
C# will immediately say: 'Dog.MakeSound()': no suitable method found to override
2. Signatures don't match
Make sure the method name, return type, and parameters match:
public class Animal
{
public virtual void MakeSound() { ... }
}
public class Dog : Animal
{
// Error: signature is different (for example, added a parameter)
public override void MakeSound(string sound)
{
Console.WriteLine(sound);
}
}
3. Don't use new instead of override unless you really mean it
The new keyword lets you hide a base class method, but that's not overriding and doesn't work via dynamic polymorphism. It's a different mechanism, and you should usually avoid it unless you have a good reason.
GO TO FULL VERSION