42. Override vs Overload in C#
Level: Intermediate
Goal: Understand the difference between two ways to reuse method names - compile-time vs runtime.
Overload (compile-time): Same name, different parameters
void Calculate(int a, int b) { }
void Calculate(double a, double b) { }
void Calculate(string a) { }
Override (runtime): Same name, inherits from parent
class Person { virtual void GetGrade() { } }
class Student : Person { override void GetGrade() { } }
Most confused OOP concept.
- Method overloading - same name, different parameters
- Method overriding - parent/child, same signature
- Compile-time vs runtime polymorphism
- Virtual dispatch - how C# picks which method to run
- When to use overloading (convenience methods)
- When to use overriding (true polymorphism)
- Common mistakes and design patterns
- Interview-ready explanations
Override vs Overload Reference
| Overloading | Overriding | |
|---|---|---|
| Location | Same class | Parent ? Child class |
| Method name | Same | Same |
| Parameters | ? Different | ? Same |
| Return type | Can differ | Same (or covariant) |
| Keywords | None | virtual + override |
| Base class | N/A | Must be virtual |
| Resolved at | Compile-time | Runtime |
| Polymorphism | Ad-hoc (static) | True (dynamic/virtual) |
| Example | Func(int), Func(string) | virtual void Func(), override void Func() |
| Use case | Multiple input types | Different behavior per class |
Method Overloading - Same Name, Different Parameters
Same class. Same method name. Different parameter signatures.
public class ReportService
{
// Overload 1 - name only
public void GenerateReport(string studentName)
{
Console.WriteLine($"Report for: {studentName}");
}
// Overload 2 - name + class
public void GenerateReport(string studentName, string className)
{
Console.WriteLine($"Report: {studentName} | Class: {className}");
}
// Overload 3 - name + class + marks array
public void GenerateReport(string studentName, string className, int[] marks)
{
double avg = marks.Average();
Console.WriteLine($"Report: {studentName} | {className} | Avg: {avg:F1}");
}
// Overload 4 - Student object
public void GenerateReport(Student student)
{
Console.WriteLine($"Report: {student.Name} | {student.Percentage:F1}%");
}
// ? Return type ALONE cannot distinguish overloads
// [X] This is NOT valid overloading:
// public string GenerateReport(string name)
// {
// return name; // error - same params as #1
// }
}
// Compiler picks correct overload based on arguments
var svc = new ReportService();
svc.GenerateReport("Sahasra"); // calls overload 1
svc.GenerateReport("Sahasra", "10th"); // calls overload 2
svc.GenerateReport("Sahasra", "10th", new[]{82,91,78}); // calls overload 3
svc.GenerateReport(student); // calls overload 4
Method Overriding - Replace Base Class Behavior
Different classes (parent/child). Same method name AND parameters. Override behavior.
public class Person
{
public string Name { get; set; } = "";
// virtual - CAN be overridden
public virtual string GetRole()
{
return "Person";
}
public virtual void PrintSummary()
{
Console.WriteLine($"[Person] {Name}");
}
}
public class Student : Person
{
public double Percentage { get; set; }
// override - REPLACES parent method
public override string GetRole() => "Student";
public override void PrintSummary()
=> Console.WriteLine($"[Student] {Name} | {Percentage:F1}%");
}
public class Teacher : Person
{
public string Subject { get; set; } = "";
public override string GetRole() => "Teacher";
public override void PrintSummary()
=> Console.WriteLine($"[Teacher] {Name} | {Subject}");
}
// Runtime dispatches to correct override
List<Person> members = new()
{
new Student { Name="Sahasra", Percentage=87.5 },
new Teacher { Name="Dr. Mehta", Subject="Maths" },
};
foreach (var m in members)
m.PrintSummary(); // each gets its own version
// [Student] Sahasra | 87.5%
// [Teacher] Dr. Mehta | Maths
Side-by-Side Comparison
| Overloading | Overriding | |
|---|---|---|
| Where | Same class | Parent ? Child |
| Method name | Same | Same |
| Parameters | Different | Same |
| Return type | Can differ | Same (or covariant) |
| Keywords | None | virtual + override |
| Resolved at | Compile time | Runtime |
| Purpose | Multiple ways to call | Replace parent behavior |
| Polymorphism | Ad-hoc | Runtime |
Common Mistakes
? Changing parameters - thinking it-s overriding (it-s overloading):
public class Person
{
public virtual void DisplayInfo()
=> Console.WriteLine("Person info");
}
public class Student : Person
{
// Wrong - different parameters, not overriding
public override void DisplayInfo(string className) // Added param
=> Console.WriteLine($"Student in {className}");
}
var people = new List<Person> { new Student() };
foreach (var p in people)
p.DisplayInfo(); // Calls Person.DisplayInfo, not Student
? Match base signature exactly:
public class Student : Person
{
public override void DisplayInfo() // ? Same signature
=> Console.WriteLine("Student info");
}
foreach (var p in people)
p.DisplayInfo(); // Calls Student.DisplayInfo
? Using override without virtual in base (compile error):
public class Person
{
public void GetRole() // NOT virtual
=> Console.WriteLine("Person");
}
public class Student : Person
{
public override void GetRole() // Error - base not virtual
=> Console.WriteLine("Student");
}
? Add virtual to base method:
public class Person
{
public virtual void GetRole() // ? virtual
=> Console.WriteLine("Person");
}
public class Student : Person
{
public override void GetRole() // ? OK
=> Console.WriteLine("Student");
}
? Confusing overload and override:
public class ReportService
{
public void Generate(string name) // Overload 1
=> Console.WriteLine(name);
public void Generate(string name, int days) // Overload 2
=> Console.WriteLine($"{name} for {days} days");
}
// vs.
public class BaseReport
{
public virtual void Generate()
=> Console.WriteLine("Report");
}
public class StudentReport : BaseReport
{
public override void Generate() // Override - replaces parent
=> Console.WriteLine("Student Report");
}
? Use overloading for convenience methods in same class:
public class StudentProcessor
{
// Overload 1 - basic
public void Process(Student student)
=> Console.WriteLine(student.Name);
// Overload 2 - with details
public void Process(Student student, bool detailed)
{
Process(student); // reuse first overload
if (detailed) Console.WriteLine(student.Percentage);
}
}
? Mixing overload and override (confusing):
public class Person
{
public virtual void Print()
=> Console.WriteLine("Person");
}
public class Student : Person
{
public override void Print() // Override base
=> Console.WriteLine("Student");
public void Print(bool detailed) // Overload override
{
Print(); // Calls override
if (detailed) Console.WriteLine("Details");
}
}
? Clarify intent - document which is which:
public class Student : Person
{
// Override parent-s method (virtual dispatch)
public override void Print()
=> Console.WriteLine("Student");
// Convenience overload (compile-time)
public void PrintDetailed()
=> Print();
}
Best Practices
- Use overloading for convenience methods - Multiple ways to call same logic
- Use overriding for polymorphism - Replace parent behavior in subclass
- Mark methods
virtualintentionally - Not every method should be overridable - Keep overload signatures distinct - Compiler must disambiguate easily
- Don-t overload with similar parameter types - Confusing:
Func(int)vsFunc(double) - Use same parameter names in overrides - IDE hints show consistent names
- Document virtual methods - Explain what subclasses should do
- Test polymorphism in collections - Override works through base reference
- Avoid using
newkeyword - Useoverridefor inheritance - Consider sealed classes if not extensible - Prevent accidental overrides
- Use abstract methods for must-override - Force derived classes to implement
- Keep method complexity reasonable - Complex methods hard to override correctly
When You'll Use This in SMS
SMS uses both overloading and overriding:
// Overloading - same name, different params
public class StudentService
{
public Student GetStudent(int id) => _repo.GetById(id);
public Student GetStudent(string rollNumber) => _repo.GetByRoll(rollNumber);
}
// Overriding - replace base method
public class Student : BaseEntity
{
public override void Validate()
{
if (string.IsNullOrWhiteSpace(Name)) throw new Exception("Name required");
}
}
Override vs overload explained: same name different params (overload), replace base behavior (override). Video coming soon. Subscribe to NexCoding YouTube for updates.
Overloading: Same class, same name, DIFFERENT parameters. Compile-time.
Overriding: Parent?Child class, same name, SAME parameters. Runtime, requires virtual + override.
// Overloading - compile-time, same class
public class Calculator
{
public int Add(int a, int b) => a + b; // Overload 1
public double Add(double a, double b) => a + b; // Overload 2
}
// Overriding - runtime, inheritance
public class Person
{
public virtual void GetRole() => "Person";
}
public class Student : Person
{
public override void GetRole() => "Student"; // Replaces Person version
}
Rule: Overloading for convenience in same class. Overriding for polymorphism in inheritance.
No. Only virtual or abstract methods can be overridden. Compiler error otherwise.
public class Person
{
public void GetRole() { } // NOT virtual
}
public class Student : Person
{
public override void GetRole() { } // Error
}
Solution: Either make base virtual, or use new (method hiding):
public class Person
{
public virtual void GetRole() { } // ? virtual
}
public class Student : Person
{
public override void GetRole() { } // ? OK
}
// OR use new (not recommended)
public class Student : Person
{
public new void GetRole() { } // Hiding, not overriding
}
virtual signals "designed for override." Never override non-virtual method.
Compile-time. Compiler picks the overload based on parameter types AT COMPILE TIME.
public void Process(int x) => Console.WriteLine("int");
public void Process(string s) => Console.WriteLine("string");
Process(5); // Compiler sees int ? calls Process(int)
Process("hello"); // Compiler sees string ? calls Process(string)
// Compiler KNOWS which overload to use before runtime
Overriding = runtime. Runtime picks the method based on ACTUAL OBJECT TYPE.
Person p = new Student();
p.GetRole(); // Runtime checks: actual type is Student ? calls Student.GetRole()
Overload = static polymorphism (compile-time). Override = dynamic polymorphism (runtime).
Yes, valid but confusing. You can override parent-s method AND overload it.
public class Person
{
public virtual void Print() => Console.WriteLine("Person");
}
public class Student : Person
{
public override void Print() // Override parent
=> Console.WriteLine("Student");
public void Print(string extra) // Overload override
{
Print(); // Calls override above
Console.WriteLine(extra);
}
}
var s = new Student();
s.Print(); // Student.Print() ? override
s.Print("Detailed"); // Student.Print(string) ? overload
Person p = new Student();
p.Print(); // Student.Print() - override works through base reference
// p.Print("x"); // Error - overload not accessible through Person reference
Technically works but confusing. Keep separate: override parent, overload in same class.
Override when: You want different behavior in subclass AND want it called polymorphically.
// Override - PrintSummary changes per type
public class Person
{
public virtual void PrintSummary()
=> Console.WriteLine($"Person: {Name}");
}
public class Student : Person
{
public override void PrintSummary()
=> Console.WriteLine($"Student: {Name} | {Percentage:F1}%");
}
// Works in collections
foreach (Person p in new Person[] { new Student(), new Teacher() })
p.PrintSummary(); // Each calls correct version
Don-t override when: Behavior is identical or shouldn-t change per type.
Override = polymorphism. Only if behavior differs meaningfully.
1. Signature must match exactly - Same parameters, same return type (or covariant).
2. Access cannot be more restrictive - Can-t override public as private.
3. Exceptions can-t be broader - Can-t override throwing checked exception with unchecked.
4. Base method must be virtual or abstract.
public class Person
{
public virtual void Print(string name)
=> Console.WriteLine(name);
}
public class Student : Person
{
public override void Print(string name) // ? Exact match
=> Console.WriteLine($"Student: {name}");
// Error - different signature
// public override void Print(string name, int id) { }
// Error - more restrictive access
// private override void Print(string name) { }
}
Follow Liskov Substitution Principle: subclass can be used anywhere base is used.
Use ChatGPT, Claude, or Copilot to go deeper on C# method overloading vs overriding. Try these prompts:
"Explain method overloading vs overriding in C# - what is the key difference?""Can I override a non-virtual method in C#? What happens?""Is method overloading compile-time or runtime polymorphism in C#?""Quiz me: show code and ask whether it is overloading or overriding"
💡 Tip: After reading this article, paste your own code into AI and ask "What could go wrong here and why?" — fastest way to find edge cases and deepen understanding.