Skip to main content

39. Method Hiding in C#

Level: Advanced
Goal: Understand the subtle but critical difference between override and new keywords.

Two inheritance problems:

Base class has GetGrade()
Derived class wants its own GetGrade()

Use override or new?
  • override: Correct OOP behavior (polymorphism)
  • new: Hides the base method (usually wrong)

Most common interview question.

ℹ️ What You'll Learn
  • override - runtime polymorphism, virtual dispatch
  • new - compile-time method hiding, shadowing
  • Virtual method resolution - how C# decides which method to call
  • Reference type vs actual object type - critical difference
  • When to use override (almost always)
  • When to use new (rarely, design smell)
  • Common gotchas and avoiding hidden bugs
  • Interview-ready explanations

Method Resolution Reference

Aspectoverride (recommended)new (method hiding)
Base methodMust be virtual or abstractCan hide any inherited method
Which runsMost derived type (at runtime)Reference type (at compile-time)
DispatchVirtual dispatch (late binding)Static dispatch (early binding)
OOP DesignCorrect polymorphismUsually signals a design problem
PolymorphicWorks correctly in base-class collectionsCan break polymorphism
Examplepublic override void Method()public new void Method()
Use CaseExtend base behaviorRarely (avoid if possible)
InterviewAlways mention thisShow understanding of danger

When You'll Use This in SMS

SMS avoids method hiding:

// Wrong BAD - method hiding (confusing)
public class Person { public virtual void Display() => Console.WriteLine("Person"); }
public class Teacher : Person { public new void Display() => Console.WriteLine("Teacher"); } // Hides!

// OK GOOD - method override (polymorphic)
public class Student : Person
{ public override void Display() => Console.WriteLine("Student"); }

Person person = new Student();
person.Display(); // Calls Student.Display (override)

ℹ️ Video Tutorial

Method hiding vs override: new keyword, virtual/override, polymorphism, why hiding is dangerous. Video coming soon. Subscribe to NexCoding YouTube for updates.


override - Runtime Polymorphism

When base class method is virtual and derived class uses override - the most derived version ALWAYS runs regardless of reference type.

public class Person
{
public virtual void DisplayInfo()
=> Console.WriteLine($"Person: {GetType().Name}");
}

public class Student : Person
{
public override void DisplayInfo()
=> Console.WriteLine($"Student: Sahasra Kumar | 10th Grade");
}

public class Teacher : Person
{
public override void DisplayInfo()
=> Console.WriteLine($"Teacher: Dr. Mehta | Mathematics");
}

// OVERRIDE - virtual dispatch - runs ACTUAL type's method
Person person = new Student(); // Student stored in Person reference
person.DisplayInfo(); // "Student: Sahasra Kumar | 10th Grade"
// Runtime checks: actual object is Student -> calls Student.DisplayInfo

Person person2 = new Teacher();
person2.DisplayInfo(); // "Teacher: Dr. Mehta | Mathematics"

new - Method Hiding (Compile-Time)

new hides the base class method. Which method runs depends on the reference type, not the actual object.

public class Person
{
public void DisplayInfo() // NOT virtual
=> Console.WriteLine($"Person: {GetType().Name}");
}

public class Student : Person
{
public new void DisplayInfo() // HIDES base method
=> Console.WriteLine($"Student: Sahasra Kumar | 10th Grade");
}

// Method hiding - reference type determines which method runs
Student studentRef = new Student();
studentRef.DisplayInfo(); // "Student: Sahasra Kumar" - Student reference -> Student method

Person personRef = new Student(); // Student stored in Person reference
personRef.DisplayInfo(); // "Person: Student" <- !!!
// Compile-time: reference is Person -> calls Person.DisplayInfo
// Does NOT call Student.DisplayInfo even though object IS a Student

Side-by-Side Comparison

public class SchoolMember
{
public virtual void VirtualMethod() => Console.WriteLine("SchoolMember.Virtual");
public void RegularMethod() => Console.WriteLine("SchoolMember.Regular");
}

public class Student : SchoolMember
{
public override void VirtualMethod() => Console.WriteLine("Student.Override");
public new void RegularMethod() => Console.WriteLine("Student.New (hiding)");
}

SchoolMember member = new Student(); // Student in SchoolMember reference

member.VirtualMethod(); // "Student.Override" <- override wins (runtime dispatch)
member.RegularMethod(); // "SchoolMember.Regular" <- new hides (compile-time, uses ref type)

Student student = new Student();
student.VirtualMethod(); // "Student.Override"
student.RegularMethod(); // "Student.New (hiding)" <- Student ref sees Student method

School Management - Real Scenario

💻 Try It — Console App
💡 Paste into Program.cs and press F5⌥ GitHub
public class Person
{
public string Name { get; set; } = "";

public virtual string GetRole() => "Person";

public virtual void PrintCard()
=> Console.WriteLine($"[PERSON] {Name}");
}

public class Student : Person
{
public string ClassName { get; set; } = "";
public double Percentage { get; set; }

// OK: OVERRIDE - polymorphic, always called on Student objects
public override string GetRole() => "Student";

public override void PrintCard()
=> Console.WriteLine($"[STUDENT] {Name} | {ClassName} | {Percentage:F1}%");
}

public class GuestLecturer : Person
{
// [!] HIDING - only called when reference is GuestLecturer
public new void PrintCard()
=> Console.WriteLine($"[GUEST] {Name}");

public override string GetRole() => "Guest Lecturer";
}

// Override works correctly with collections
var schoolMembers = new List<Person>
{
new Student { Name = "Sahasra", ClassName="10th", Percentage=87.5 },
new Student { Name = "Priya", ClassName="11th", Percentage=91.0 },
new GuestLecturer { Name = "Dr. Kumar" },
};

// override - correct method called for each type
foreach (var member in schoolMembers)
member.PrintCard();
// [STUDENT] Sahasra | 10th | 87.5%
// [STUDENT] Priya | 11th | 91.0%
// [PERSON] Dr. Kumar <- GuestLecturer.PrintCard NOT called (hiding, not override)

Common Mistakes

Problem: Using new instead of override (breaks polymorphism):

public class Person
{
public virtual void DisplayInfo()
=> Console.WriteLine("Person");
}

public class Student : Person
{
public new void DisplayInfo() // WRONG - hiding, not overriding
=> Console.WriteLine("Student");
}

var people = new List<Person>
{
new Student { },
new Student { }
};

foreach (var person in people)
person.DisplayInfo(); // Always prints "Person" - Student version NEVER called

Fix: Use override when base is virtual:

public class Student : Person
{
public override void DisplayInfo() // CORRECT - runtime polymorphism
=> Console.WriteLine("Student");
}

foreach (var person in people)
person.DisplayInfo(); // Prints "Student" correctly

Problem: Forgetting virtual on base method, then using override:

public class Teacher
{
public void GetSalary() // NOT virtual
=> Console.WriteLine("20000");
}

public class PrincipalTeacher : Teacher
{
public override void GetSalary() // Error - base not virtual
=> Console.WriteLine("50000");
}

Fix: Make base method virtual first:

public class Teacher
{
public virtual void GetSalary() // virtual
=> Console.WriteLine("20000");
}

public class PrincipalTeacher : Teacher
{
public override void GetSalary() // OK
=> Console.WriteLine("50000");
}

Problem: Using new when you meant override in a collection:

public class SchoolMember
{
public virtual void PrintReport()
=> Console.WriteLine("Member");
}

public class Teacher : SchoolMember
{
public new void PrintReport() // Hiding - wrong in a collection
=> Console.WriteLine("Teacher Report");
}

var staff = new List<SchoolMember> { new Teacher() };
staff[0].PrintReport(); // Prints "Member" - not "Teacher Report"

Fix: Always use override for polymorphic methods:

public class Teacher : SchoolMember
{
public override void PrintReport() // Correct
=> Console.WriteLine("Teacher Report");
}

staff[0].PrintReport(); // Now prints "Teacher Report"

Problem: Hiding a method and then calling through base reference (confusing):

var teacher = new Teacher();
teacher.PrintReport(); // Teacher version (reference is Teacher)

SchoolMember member = teacher;
member.PrintReport(); // Base version (reference is SchoolMember) - unexpected!

Fix: Consistent behavior - use override:

public class Teacher : SchoolMember
{
public override void PrintReport() // Always called on Teacher objects
=> Console.WriteLine("Teacher");
}

teacher.PrintReport(); // Teacher version
member.PrintReport(); // Teacher version (correct!)

Best Practices

  1. Use override by default - 99% of the time, this is what you want
  2. Mark methods virtual intentionally - Not every method should be overridable
  3. Never use new to hide methods - If you must, it signals design problem
  4. Test polymorphic behavior in collections - Verify correct method runs on each type
  5. Use abstract for must-override methods - Force derived classes to implement
  6. Document virtual methods clearly - Explain what subclasses should do
  7. Call base.MethodName() when extending - Combine base + derived behavior
  8. Avoid new with virtual methods - Confusing and breaks polymorphism
  9. Keep method signatures identical - Same parameters, return type as base
  10. Use compiler warnings - Enable warnings for hiding base methods
  11. Design for inheritance - Virtual methods signal extension points
  12. Prefer composition over complex inheritance - Simplify polymorphic scenarios

🎯 Q1: What's the difference between override and new?

override: Base method must be virtual. Derived version ALWAYS runs regardless of reference type. Runtime polymorphism - late binding.

new: Hides base method. Which version runs depends on reference type, not actual object type. Compile-time dispatch - early binding.

// override - actual type decides
Person p = new Student();
p.DisplayInfo(); // Student version (actual type = Student)

// new - reference type decides
SchoolMember m = new Teacher();
m.PrintCard(); // Base version (reference type = SchoolMember)

Rule: override for true polymorphism, new is a code smell.

🎯 Q2: Why doesn't new work correctly in a collection?

When you store Student in Person reference, the compiler uses the reference type (Person) to determine which method to call.

With new (hiding): Calls base Person method. With override: Calls derived Student method (correct).

var people = new List<Person> { new Student(), new Teacher() };

foreach (var p in people)
p.DisplayInfo(); // With override: correct Student/Teacher methods
// With new: always base Person method (wrong!)

override fixes this - runtime checks actual object type.

🎯 Q3: When would I ever use new instead of override?

Very rarely. Only when:

  1. Base class has non-virtual method you can't modify
  2. You must provide different implementation in derived class
  3. You accept breaking polymorphism (usually wrong decision)
// Legacy code - you can't change Person class
public class Person { public void LegacyMethod() { } } // NOT virtual

public class Student : Person
{
public new void LegacyMethod() { } // Only choice if base not virtual
}

Better solution: Either make base virtual, or use composition instead of inheritance.

new signals design problem - refactor instead.

🎯 Q4: What happens if I use override without virtual on base?

Compiler error: "cannot override member that is not marked virtual, abstract, or override."

public class Base
{
public void Method() { } // NOT virtual
}

public class Derived : Base
{
public override void Method() { } // Error
}

Solution: Add virtual to base method.

public class Base
{
public virtual void Method() { } // Now override works
}

Compiler enforces correct OOP.

🎯 Q5: How do I call the base class method from derived?

Use base.MethodName() to access base implementation. Useful for extending behavior.

public class Student : Person
{
public override void DisplayInfo()
{
base.DisplayInfo(); // Call Person.DisplayInfo
Console.WriteLine("Student-specific info");
}
}

var s = new Student { Name = "Sahasra" };
s.DisplayInfo();
// Output: Person: Sahasra
// Student-specific info

base lets you combine base + derived behavior.

🎯 Q6: What's the difference between new and abstract override?

new: Hides method, compile-time dispatch. override: Runtime dispatch, calls actual type's implementation. abstract: Forces derived classes to implement the method.

// abstract - must override
public abstract class Shape
{
public abstract void Draw(); // Every Shape must implement
}

public class Circle : Shape
{
public override void Draw() => Console.WriteLine("Circle");
}

// Usage - polymorphism guaranteed
Shape s = new Circle();
s.Draw(); // Circle version (always works with override)

Abstract + override = correct polymorphism. Best practice.

🤖Use AI to Learn Faster

Use ChatGPT, Claude, or Copilot to go deeper on C# method hiding new keyword vs override. Try these prompts:

  • "What is the difference between method hiding (new) and method overriding (override) in C#?"
  • "If I use new to hide a method, what happens when I call it through a base class reference?"
  • "Why would a developer use method hiding instead of override in C#?"
  • "Quiz me: what gets printed in this method hiding vs override scenario?"

💡 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.

Next Article

Type Operators ->

nexcoding.in