Home C# / .NET C# Inheritance — Missing Override Causes Silent Failure
Intermediate 6 min · March 06, 2026

C# Inheritance — Missing Override Causes Silent Failure

Missing override on a new method silently applied base premium discount logic instead of derived.

N
Naren Founder & Principal Engineer

20+ years shipping production .NET services in enterprise systems. Lessons pulled from things that broke in production.

Follow
Production
production tested
July 18, 2026
last updated
2,466
articles · all by Naren
Before you start⏱ 25 min
  • Solid grasp of fundamentals
  • Comfortable reading code examples
  • Basic production concepts
 ● Production Incident 🔎 Debug Guide ⚙ Triage Commands
Quick Answer
  • Inheritance in C# models an "is-a" relationship between classes
  • Derived class inherits all non-private members from the base class
  • Use override to replace base behaviour; use new to hide it
  • Base constructor must be explicitly called unless it's parameterless
  • Deep hierarchies (5+ levels) are a smell — prefer composition
  • Mistaking new for override silently breaks polymorphic dispatch
✦ Definition~90s read
What is Inheritance in C#?

Inheritance is the mechanism by which a class (the derived class) can reuse and extend the members of another class (the base class). C# supports single class inheritance — a derived class can have only one direct base class. However, interfaces can be inherited multiple times.

Think of a vehicle blueprint.

The syntax uses a colon: class Derived : Base. All non-private members (fields, properties, methods, events) are inherited. Constructors and finalizers are not inherited, but the derived class must call a base constructor. The protected access modifier is commonly used to share members with derived classes while hiding them from external code.

The real power of inheritance comes from polymorphism: you can treat a derived object as an instance of its base type. Code that operates on a base class can work with any derived class without modification. This is a fundamental concept in object-oriented design.

Plain-English First

Think of a vehicle blueprint. Every vehicle has wheels, an engine, and a way to move. A car is a vehicle, but it also has a specific number of doors and a trunk. A motorcycle is also a vehicle, but it has two wheels and no roof. Inheritance lets you write the 'vehicle' stuff once and let 'Car' and 'Motorcycle' borrow it automatically — then each adds only what makes it unique. You're not copying and pasting; you're saying 'start with everything a Vehicle already knows, and add on from there.'

Every non-trivial C# application has classes that share behaviour. A Customer and an Employee both have a name, an email, and an ID. A Circle and a Rectangle both have an area and a perimeter. If you write those shared properties and methods twice, you've just signed up for double the maintenance, double the bugs, and double the pain every time requirements change. Inheritance is how C# lets you centralise what's shared and specialise what's different — and it's one of the four pillars of object-oriented programming for a very good reason.

The real problem inheritance solves isn't code length — it's conceptual integrity. When a BankAccount and a SavingsAccount share a base, you're modelling the real world accurately. A savings account IS a bank account. That 'is-a' relationship is the signal that inheritance belongs here. Without it, you end up with bloated utility classes, copy-pasted logic, and systems where fixing one bug means hunting down five nearly-identical methods spread across the codebase.

By the end of this article you'll understand how to build a clean inheritance hierarchy in C#, know the difference between virtual, override, and new (and why confusing them causes silent bugs that are a nightmare to trace), and be able to explain to an interviewer exactly when inheritance is the right tool — and when it absolutely isn't.

What is Inheritance in C#?

Inheritance is the mechanism by which a class (the derived class) can reuse and extend the members of another class (the base class). C# supports single class inheritance — a derived class can have only one direct base class. However, interfaces can be inherited multiple times. The syntax uses a colon: class Derived : Base. All non-private members (fields, properties, methods, events) are inherited. Constructors and finalizers are not inherited, but the derived class must call a base constructor. The protected access modifier is commonly used to share members with derived classes while hiding them from external code.

The real power of inheritance comes from polymorphism: you can treat a derived object as an instance of its base type. Code that operates on a base class can work with any derived class without modification. This is a fundamental concept in object-oriented design.

InheritanceDemo.csCSHARP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
using System;

namespace TheCodeForge.InheritanceDemo
{
    public class Vehicle
    {
        public string Make { get; set; }
        public string Model { get; set; }
        public int Year { get; set; }

        public Vehicle(string make, string model, int year)
        {
            Make = make;
            Model = model;
            Year = year;
        }

        public virtual void Start()
        {
            Console.WriteLine($"{Make} {Model} started.");
        }
    }

    public class Car : Vehicle
    {
        public int Doors { get; set; }

        public Car(string make, string model, int year, int doors)
            : base(make, model, year)
        {
            Doors = doors;
        }

        public override void Start()
        {
            Console.WriteLine($"{Make} {Model} (car with {Doors} doors) started.");
        }
    }

    public class Program
    {
        public static void Main()
        {
            Vehicle myVehicle = new Car("Toyota", "Camry", 2020, 4);
            myVehicle.Start();  // Output: Toyota Camry (car with 4 doors) started.
        }
    }
}
Output
Toyota Camry (car with 4 doors) started.
Mental Model
Mental Model: Inheritance as a "Is-A" Template
A derived class IS A more specific version of its base class.
  • If you can't say 'Dolphin IS A Mammal' truthfully, don't inherit.
  • The base class captures common behavior and data; the derived class specializes.
  • Polymorphism works because every derived object shares the base interface.
  • If the relationship is 'has-a' instead of 'is-a', use composition over inheritance.
📊 Production Insight
In large codebases, go-to-definition on a method might jump to the base class instead of the derived override.
Use 'Call Hierarchy' or 'Find All References' to see the actual implementation invoked at runtime.
If you're debugging a polymorphic call, check the runtime type with GetType() — don't trust the compile-time type alone.
🎯 Key Takeaway
Inheritance is about reuse of contract, not just implementation.
If you reach for inheritance only to reuse code, reconsider — composition is often safer.
The test: ask yourself 'Is Derived truly a subtype of Base?'
inheritance-csharp C# Inheritance Hierarchy Layers From base class to sealed class with virtual and override Abstract Base AbstractClass | AbstractMethod() Virtual Methods VirtualMethod() | Default Implementation Derived Class OverrideMethod() | NewMethod() Sealed Class SealedClass | Cannot Inherit Client Code BaseTypeRef | PolymorphicCall THECODEFORGE.IO
thecodeforge.io
Inheritance Csharp

Base and Derived Classes: Syntax and Essentials

To create a derived class, place a colon and the base class name after the derived class name. The derived class automatically inherits all non-private members. However, constructors are not inherited: you must explicitly call a base constructor using : base(...). If the base class has a parameterless constructor, it's called implicitly — but it's still good practice to be explicit.

Access modifiers control visibility to derived classes. Use protected for members that should be accessible to derived types but not to external code. Use private protected (C# 7.2+) to allow access only to derived classes that are in the same assembly.

The base keyword inside a derived class allows you to access base class members even if they are overridden or hidden. It's especially useful when you want to extend a base method instead of replacing it completely.

BaseKeywordDemo.csCSHARP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
using System;

namespace TheCodeForge.InheritanceDemo
{
    public class Animal
    {
        public string Name { get; set; }

        public Animal(string name)
        {
            Name = name;
        }

        public virtual void Speak()
        {
            Console.WriteLine("Animal makes a sound.");
        }
    }

    public class Dog : Animal
    {
        public Dog(string name) : base(name) { }

        public override void Speak()
        {
            base.Speak();  // Optional: call base implementation
            Console.WriteLine($"{Name} barks!");
        }
    }

    class Program
    {
        static void Main()
        {
            Dog dog = new Dog("Rex");
            dog.Speak();
        }
    }
}
Output
Animal makes a sound.
Rex barks!
📊 Production Insight
When a base constructor throws, the derived constructor never runs. This can leave members uninitialized.
A common pattern is to validate parameters in both constructors, but the base constructor runs first.
If the base constructor is expensive (e.g., DB calls), consider lazy initialization or factory methods.
🎯 Key Takeaway
Always call a base constructor explicitly unless it's parameterless.
Use base.MethodName() to extend base behaviour cleanly.
Remember: the base class constructor runs before the derived class body.

Virtual, Override, and New: The Critical Difference

The virtual keyword in the base class declares a method that can be overridden by a derived class. The override keyword in the derived class replaces the base implementation for all callers, including those using the base type reference. This is the heart of polymorphism.

The new keyword, when applied to a method with the same signature, hides the base method instead of overriding it. This means that the method called depends on the compile-time type of the variable, not the runtime type. This is a common source of subtle bugs.

A method that is not marked virtual cannot be overridden. If you attempt to use override on a non-virtual method, you get a compile-time error. You can still use new to shadow it, but that's usually a design smell.

When you override a method, you can call the base implementation using base.MethodName(). This is useful when you want to add behaviour on top of the base class logic.

⚠ Warning: Don't Confuse `new` with `override`
Using new when you intended polymorphism results in the wrong method being called when the object is treated as its base type. This bug is silent — no compile-time warning by default. Enable IDE warning CS0109 and treat it as an error.
📊 Production Insight
Teams frequently debug 'method not called' for hours only to find new instead of override.
The fix: enable .editorconfig rule dotnet_diagnostic.CS0109.severity = error.
In a large service with many derived types, this one mistake can cause data corruption due to incorrect logic path.
🎯 Key Takeaway
Override changes behaviour for everyone; new only for your own type.
When in doubt, use override.
Enable the compiler warning for 'new' — it's a red flag.
When to Use Virtual/Override vs New
IfDerived class should always replace base behaviour, even via base reference
UseUse virtual in base, override in derived
IfDerived class only wants to shadow base method for direct calls
UseUse new — but only if you understand the break in polymorphism
IfBase method is not virtual and you cannot change it
UseMust use new — but reconsider design; consider composition
inheritance-csharp THECODEFORGE.IO C# Inheritance Hierarchy Layers From abstract base to sealed classes with virtual dispatch Abstract Base Class AbstractMethod() | VirtualMethod() Intermediate Derived Class Override VirtualMethod() | New HideMethod() Concrete Derived Class Override OverrideMethod() | New AnotherHide() Sealed Class Cannot inherit further | Final implementation Client Code Base reference | Polymorphic call THECODEFORGE.IO
thecodeforge.io
Inheritance Csharp

Constructors and Inheritance: Base Constructor Calls

Constructors are not inherited, but every derived class must ensure that the base class constructor is called. If the base class has a parameterless constructor, it's called implicitly. If the base class only has parameterized constructors, the derived class must explicitly call one using the colon syntax: public Derived(params) : base(params).

The base constructor runs before the derived constructor body. This means if you have initialization logic in both, the base runs first. Understanding this order is critical when properties are set in the base constructor and then modified in the derived constructor.

If the base constructor throws an exception, the derived constructor will not execute. This is a common cause of NullReferenceException when the derived class's field initializers run after the base constructor call (C# initializes fields before the base constructor call, but that's a subtle point).

🔥Did You Know?
Field initializers in the derived class run before the base constructor. That's why you can't use derived instance fields in base constructor calls — they haven't been assigned yet.
📊 Production Insight
If a base constructor does heavy work (e.g., database lookup) and the derived class doesn't need it, you waste resources.
Consider having a protected parameterless constructor in the base for derived classes that can skip heavy initialization.
Another pattern: use factory methods that allow the derived class to control initialization order.
🎯 Key Takeaway
Always explicitly call a base constructor — never rely on implicit parameterless calls if your base has no default.
Field initializers run before base constructor — watch the order.
If base constructor fails, derived is never fully initialized.

Abstract Classes and Sealed Classes

An abstract class cannot be instantiated directly; it serves as a base class that defines a contract and optionally provides common implementation. Abstract methods have no body and must be overridden in any non-abstract derived class. Abstract classes can also have virtual methods, fields, constructors, etc.

A sealed class prevents further inheritance. It cannot be used as a base class. Sealing a class can help with security, performance (the JIT can devirtualize sealed class method calls), and design clarity.

Abstract classes are often used when you want to force derived classes to implement certain methods while providing shared base logic. They are similar to interfaces but can contain state (fields, properties with backing fields) and method bodies.

C# also allows you to seal individual methods in a derived class, preventing further overriding down the hierarchy.

AbstractSealedDemo.csCSHARP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
using System;

namespace TheCodeForge.InheritanceDemo
{
    public abstract class Shape
    {
        public abstract double Area();

        public void Display()
        {
            Console.WriteLine($"Area: {Area():F2}");
        }
    }

    public sealed class Circle : Shape
    {
        public double Radius { get; }

        public Circle(double radius)
        {
            Radius = radius;
        }

        public override double Area() => Math.PI * Radius * Radius;
    }

    // This would cause compile error: 'cannot derive from sealed type'
    // public class ColoredCircle : Circle { }

    class Program
    {
        static void Main()
        {
            Shape shape = new Circle(5);
            shape.Display(); // Output: Area: 78.54
        }
    }
}
Output
Area: 78.54
💡Pro Tip: Prefer Interfaces Over Abstract Classes for Contracts
Use abstract classes when you want to share implementation. Use interfaces (with default implementations if needed) when you only want to define a contract. C# allows multiple interface inheritance but only single class inheritance.
📊 Production Insight
Sealing a class can give the JIT more optimization freedom. If a class is internal and never instantiated polymorphically, seal it.
Abstract classes with many abstract methods become hard to maintain because every new method forces changes in all derived classes.
Consider the Template Method pattern via abstract classes to enforce a skeleton algorithm in derived classes.
🎯 Key Takeaway
Abstract classes define a base with shared code and required overrides.
Sealed classes prevent inheritance for security and performance.
Think twice before adding a new abstract method — you'll break all existing derived classes.

The “Is-A” Rule: Why You’ll Burn Yourself Without It

Inheritance isn't a code-sharing shortcut. It models an “is-a” relationship. A Dog is an Animal. A Manager is an Employee. If that sentence sounds wrong in plain English, don't use inheritance. I've cleaned up diffs where a junior inherited from List<T> just to get Add() for free. That's not an “is-a”; that's a design debt. The base class defines a contract. If your derived type can't fulfill every public member of the base without lying, you need composition or interfaces instead. Nothing kills a codebase faster than a Rectangle inheriting from Square because some tutorial said “reuse.” Real production: I once saw a SqlConnection wrapper inherit from Stream because the original author wanted Read(). Result: phantom open connections and NotSupportedException everywhere. Ask yourself: “Will this derived class ever violate a base class invariant?” If yes, stop. Use an interface. The compiler won't save you from bad semantics.

IsAExample.csCSHARP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// io.thecodeforge
using System;

public class Employee
{
    public string Name { get; set; }
    public decimal CalculatePay() => 5000m;
}

public class Manager : Employee
{
    public decimal Bonus { get; set; }
    // Manager IS-A Employee — bonus extends pay, doesn't break it.
}

public class Client
{
    public string Name { get; set; }
    // NOT inheritance — a client is NOT an employee.
    // Use composition instead: public Employee? AccountManager { get; set; }
}

class Program
{
    static void Main()
    {
        Manager m = new Manager { Name = "Alice", Bonus = 2000m };
        Employee e = m; // Upcast works — Manager IS-A Employee.
        Console.WriteLine($"Pay: {e.CalculatePay()}"); // 5000 — invariant preserved.
    }
}
Output
Pay: 5000
⚠ Production Trap:
If you inherit for “code reuse” alone, you'll couple your derived class to the base's implementation. Refactoring the base breaks every child. Always prefer composition over inheritance unless the “is-a” test passes.
🎯 Key Takeaway
If the 'is-a' sentence sounds wrong in English, don't write the colon.

Types of Inheritance C# Actually Gives You (and One It Doesn't)

C# supports exactly three inheritance shapes directly. Single: one class, one parent. Multilevel: A -> B -> C — the chain you see in ORM entities or layered services. Hierarchical: one base, multiple children (e.g., PaymentMethod with CreditCard and PayPal). That's it. No multiple inheritance of classes. The compiler enforces this because diamond problems cause runtime heisenbugs. The other patterns you need? Interfaces. They solve the “what” without the “how” coupling. You can implement IDisposable, IComparable, and ISerialize on the same class—that's multiple inheritance of contract. In .NET 8, you can also do default interface methods for shared behavior without the base class baggage. Production example: a NotificationService that implements both IEmailSender and ISmsSender. No shared state, no base class coupling, just clean polymorphism. If you need true multiple inheritance of behavior, stop and refactor to composition or facade pattern.

InheritanceTypes.csCSHARP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// io.thecodeforge
using System;

// Single
public class Animal { }
public class Dog : Animal { }

// Multilevel
public class Vehicle { }
public class Car : Vehicle { }
public class Tesla : Car { }

// Hierarchical
public class PaymentMethod
{
    public abstract bool ProcessPayment(decimal amount);
}
public class CreditCard : PaymentMethod
{
    public override bool ProcessPayment(decimal amount) 
    { 
        Console.WriteLine("Credit card processed");
        return true; 
    }
}
public class PayPal : PaymentMethod
{
    public override bool ProcessPayment(decimal amount) 
    { 
        Console.WriteLine("PayPal processed");
        return true; 
    }
}

class Program
{
    static void Main()
    {
        PaymentMethod pm = new CreditCard();
        pm.ProcessPayment(100m); // Polymorphic dispatch.
    }
}
Output
Credit card processed
🔥Design Decision:
Don't build deep inheritance chains (depth > 3). They become brittle. Prefer flat hierarchies with interfaces for behavior. A 5-level chain is a maintenance nightmare waiting to happen.
🎯 Key Takeaway
C# gives you single, multilevel, and hierarchical. For everything else, use interfaces or composition.

Sealed Record Types as Inheritance Alternative

C# 9 introduced record types, which are reference types with built-in value semantics. Sealed record types offer a compelling alternative to traditional inheritance when you need immutable data structures with structural equality. Unlike classes, records are designed for data-centric scenarios and cannot be inherited from when sealed. This prevents the fragile base class problem and ensures predictable behavior. To create a sealed record, use the sealed modifier on the record declaration. For example:

``csharp public sealed record Person(string FirstName, string LastName); ``

This record cannot be extended, but you can still use composition to add behavior. Records also support with expressions for non-destructive mutation, making them ideal for DTOs and domain events. In production, prefer sealed records over inheritance when you need immutable data carriers. They eliminate the need for complex inheritance hierarchies and reduce bugs from unintended polymorphism.

SealedRecordExample.csCSHARP
1
2
3
4
5
6
7
8
9
public sealed record Person(string FirstName, string LastName);

// Usage
var person1 = new Person("John", "Doe");
var person2 = person1 with { LastName = "Smith" };
Console.WriteLine(person1 == person2); // False (value equality)

// Cannot inherit: error CS0509
// public record Employee : Person { }
🔥Sealed Records vs Abstract Classes
📊 Production Insight
In production, use sealed records for DTOs, API responses, and domain events. They reduce bugs from unintended inheritance and simplify equality checks.
🎯 Key Takeaway
Sealed record types provide a safe, immutable alternative to inheritance for data-centric types, preventing fragile base class issues.

Default Interface Methods for Virtual Extension (C# 8+)

C# 8 introduced default interface methods, allowing interfaces to provide default implementations for their members. This enables a form of virtual extension without requiring base class inheritance. You can add new methods to an interface without breaking existing implementations. For example:

```csharp public interface ILogger { void Log(string message); void LogError(string message) => Log($"ERROR: {message}"); }

public class ConsoleLogger : ILogger { public void Log(string message) => Console.WriteLine(message); // LogError is inherited from the interface } ```

Classes that implement the interface can override the default method if needed. This is particularly useful for adding optional behavior to existing interfaces. However, default interface methods can lead to the diamond problem if multiple interfaces provide conflicting defaults. In production, use them sparingly to evolve public APIs without breaking changes. They are not a replacement for abstract classes but a tool for API versioning.

DefaultInterfaceMethods.csCSHARP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public interface ILogger
{
    void Log(string message);
    void LogError(string message) => Log($"ERROR: {message}");
}

public class ConsoleLogger : ILogger
{
    public void Log(string message) => Console.WriteLine(message);
}

// Usage
ILogger logger = new ConsoleLogger();
logger.LogError("Something went wrong"); // Output: ERROR: Something went wrong
⚠ Diamond Problem Risk
📊 Production Insight
Use default interface methods to extend public interfaces in libraries. Avoid overuse; prefer abstract classes for complex shared logic.
🎯 Key Takeaway
Default interface methods allow adding virtual methods to interfaces without breaking existing implementations, useful for API evolution.
Override vs New: Polymorphic Behavior How method resolution differs in C# inheritance Override New Keyword override new Polymorphism Supports polymorphic dispatch Breaks polymorphism Base Reference Call Calls derived method Calls base method Compiler Warning No warning (intended) Warning CS0108 Use Case Extend or replace behavior Hide base method intentionally THECODEFORGE.IO
thecodeforge.io
Inheritance Csharp

Inheritance vs Composition vs Records Decision Guide

Choosing between inheritance, composition, and records depends on your design goals. Inheritance models an "is-a" relationship and is best for polymorphic behavior, but it creates tight coupling and fragile base classes. Composition models "has-a" relationships, promoting loose coupling and flexibility. Records are ideal for immutable data with value semantics. Use this guide:

  • Inheritance: Use when you need polymorphic behavior and shared logic (e.g., base class with virtual methods). Avoid deep hierarchies.
  • Composition: Use when you want to reuse behavior without coupling (e.g., dependency injection, strategy pattern). Prefer over inheritance for most scenarios.
  • Records: Use for immutable data carriers (e.g., DTOs, events). Sealed records prevent inheritance.

Example: A Car class could inherit from Vehicle (is-a) or compose an Engine (has-a). For data, use a CarRecord record. In production, favor composition over inheritance to reduce complexity. Use records for data transfer and domain events.

DecisionGuideExample.csCSHARP
1
2
3
4
5
6
7
8
9
10
// Inheritance
public class Vehicle { public virtual void Start() { } }
public class Car : Vehicle { public override void Start() { /* ... */ } }

// Composition
public class Engine { public void Ignite() { } }
public class Car { private Engine _engine = new(); public void Start() => _engine.Ignite(); }

// Record
public record CarRecord(string Make, string Model);
💡Favor Composition
📊 Production Insight
In production, start with composition and records. Only use inheritance when you need polymorphic behavior and shared state. Keep hierarchies shallow.
🎯 Key Takeaway
Prefer composition over inheritance for flexibility; use records for immutable data; inheritance is best for polymorphic hierarchies.
● Production incidentPOST-MORTEMseverity: high

Missing `override` Keyword Causes Silent Logic Failure

Symptom
Premium discount was never applied, but no compile-time or runtime error occurred. The discount method ran the base version instead of the derived override.
Assumption
The team assumed that any method with the same name and signature in a derived class would automatically be called when the object is used polymorphically (e.g., through a base class reference).
Root cause
The derived class declared the method with new instead of override. The base method was not marked virtual, so override wasn't possible. When called via a base-class reference (Base ref = new Derived()), the base method was invoked because new does not participate in polymorphism.
Fix
1. Mark the base method as virtual. 2. Use override in the derived class. 3. Alternatively, ensure all callers use the derived type directly (defeats polymorphism).
Key lesson
  • Always mark methods that are meant to be polymorphically overridden as virtual.
  • In the derived class, always use override — never new — unless you explicitly want to hide the base implementation (rare).
  • Enable compiler warnings about 'new' keyword usage (CS0109) and treat them as errors in CI.
Production debug guideDiagnose inheritance-related bugs fast in production4 entries
Symptom · 01
Polymorphic call doesn't execute expected derived method
Fix
Check if base method is virtual. Check derived method uses override, not new. Use is and as to confirm runtime type.
Symptom · 02
Base class null reference in constructor
Fix
Verify that derived constructor calls base constructor with valid arguments. Look for : base(...) syntax and ensure parameters are not null.
Symptom · 03
Cannot convert derived type to base type in generic context
Fix
Check if covariance/contravariance is needed. Ensure the generic parameter is marked out for covariance.
Symptom · 04
Accessing base member yields unexpected value
Fix
If derived class shadows a field with new, both base.field and derived field exist separately. Use base.field to explicitly access base value.
★ Inheritance Quick Debug Cheat SheetFive common inheritance problems and their immediate fix
Derived method not called via interface or base reference
Immediate action
Check for `new` vs `override`
Commands
Search code: `grep -rn "new.*MethodName" ./src`
Add `override` and make base method `virtual`
Fix now
Mark base method as virtual and derived as override. Recompile.
Base constructor throws NullReferenceException+
Immediate action
Inspect derived constructor initializer
Commands
Examine `: base(...)` parameters
Add null checks before passing to base
Fix now
Guard parameters: : base(param ?? throw new ArgumentNullException(...))
Abstract class instantiation at runtime+
Immediate action
You can't — find the concrete derived class
Commands
Use `is` pattern to check actual type: `if (obj is ConcreteType)`
Refactor to use factory pattern
Fix now
Replace new AbstractClass() with factory method returning derived instance
Sealed class used as base unexpectedly+
Immediate action
Remove `sealed` from class declaration or redesign
Commands
Check class definition for `sealed` keyword
If you can't modify, use composition (wrap the sealed class)
Fix now
Remove sealed if appropriate; else create a wrapper class that contains the sealed class instance
Method signature mismatch in derived class+
Immediate action
Use `override` with exact same signature
Commands
Check base method signature (parameter types, return type)
Use IDE to generate override: type `override` and space
Fix now
Delete mismatched method and regenerate correct signature
Inheritance Keywords Comparison
KeywordEffectPolymorphism?Typical Use Case
virtualAllows derived class to overrideYesBase methods expected to be customized
overrideReplaces base virtual methodYesProviding specific behavior
newHides base method (shadowing)NoWhen you must keep same name but don't want polymorphism (rare)
abstractNo implementation; forces derived overrideYes (when overridden)Contract requiring derived to provide behavior
sealedPrevents further override or inheritanceN/ASecurity, performance, design clarity
⚙ Quick Reference
8 commands from this guide
FileCommand / CodePurpose
InheritanceDemo.csusing System;What is Inheritance in C#?
BaseKeywordDemo.csusing System;Base and Derived Classes
AbstractSealedDemo.csusing System;Abstract Classes and Sealed Classes
IsAExample.csusing System;The “Is-A” Rule
InheritanceTypes.csusing System;Types of Inheritance C# Actually Gives You (and One It Doesn
SealedRecordExample.cspublic sealed record Person(string FirstName, string LastName);Sealed Record Types as Inheritance Alternative
DefaultInterfaceMethods.cspublic interface ILoggerDefault Interface Methods for Virtual Extension (C# 8+)
DecisionGuideExample.cspublic class Vehicle { public virtual void Start() { } }Inheritance vs Composition vs Records Decision Guide

Key takeaways

1
Inheritance models an 'is-a' relationship; if that doesn't hold, use composition.
2
Use override for polymorphic behaviour, never new unless you know what you're doing.
3
Always call base constructor explicitly using `
base(...)`.
4
Seal classes and methods unless you explicitly expect inheritance.
5
Deep inheritance hierarchies are a design smell; prefer flat structures.
INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

Q01SENIOR
Explain the difference between `override` and `new` in C#.
Q02SENIOR
How does constructor chaining work in inheritance? Can a derived class p...
Q03SENIOR
What is the difference between an abstract class and an interface in C#?
Q04JUNIOR
Can you override a method that is not marked `virtual` in the base class...
Q01 of 04SENIOR

Explain the difference between `override` and `new` in C#.

ANSWER
override is used when a derived class wants to replace a base class virtual method such that the derived implementation is called even when the object is referenced via a base-typed variable. It participates in polymorphism. new hides the base method; the method called depends on the compile-time type. If you have a Base variable pointing to a Derived object, calling a method declared with new in Derived will invoke the Base method, not the Derived one. Use override for polymorphic behavior, new only when you explicitly want to shadow the base method (rarely).
FAQ · 5 QUESTIONS

Frequently Asked Questions

01
What is inheritance in C# in simple terms?
02
Can a class inherit from multiple classes in C#?
03
What is the base keyword used for in C#?
04
What is the difference between hiding and overriding?
05
When should I use abstract classes vs interfaces?
N
Naren Founder & Principal Engineer

20+ years shipping production .NET services in enterprise systems. Lessons pulled from things that broke in production.

Follow
Verified
production tested
July 18, 2026
last updated
2,466
articles · all by Naren
🔥

That's OOP in C#. Mark it forged?

6 min read · try the examples if you haven't

Previous
Classes and Objects in C#
2 / 10 · OOP in C#
Next
Interfaces in C#