Inheritance lets one class build on another. The derived class gets the base class's members, adds its own, and may replace the behavior the base class allows it to replace. Combined with virtual methods, it gives you polymorphism: code written against the base class runs the right derived behavior without knowing which derived class it has.
Deriving a class
Put the base class after a colon. The derived class has everything the base class has, plus what it declares:
Output:
TX-19: 20 km, 2 fares
True
What a derived class does and does not get:
- Inherited: fields, properties, methods, events and nested types. Everything is physically in the object.
- Accessible: only what the base allows:
public,protectedandinternalmembers. Aprivatemember ofVehicleexists inside everyTaxibutTaxi's code cannot name it. That is whyKmhas aprivate set:Taxican read it, and changes it only throughDrive. - Not inherited: constructors.
Taximust declare its own and chain to one ofVehicle's with: base(plate). The constructors page shows the order the two run in.
Every class ultimately derives from object, which is why every object has ToString(), Equals() and GetHashCode().
virtual and override
A base class marks a method virtual to say "derived classes may provide their own version". A derived class replaces it with override. Inside the override, base.Method() calls the base class's version.
Output:
[email] to lea@example.com: Your order 1042 has shipped today
(unsubscribe link appended)
[sms] to +351 912 000 111: Your order 1042 has...
[generic] to ops-team: Your order 1042 has shipped today
The loop variable is typed Notification, and yet each object renders its own way. That is polymorphism: the call n.Render(...) is resolved at run time from the object's actual type. Notice also that base.Render in Email uses Channel, which is itself virtual, so the base method prints email, not generic. A virtual call inside the base class still reaches the override.
Properties can be virtual too, as Channel shows. Fields cannot.
The compiler holds you to both keywords. Writing override on a method that is not virtual is error CS0506 ("cannot override inherited member ... because it is not marked virtual, abstract, or override"). Leaving override off when the base method is virtual is only a warning, and it changes the meaning completely, as the next section shows.
new vs override: hiding instead of overriding
If a derived class declares a method with the same signature as a base method without writing override, it hides the base method. The compiler warns (CS0114 for a virtual base method, CS0108 otherwise) and suggests the new keyword, which silences the warning but keeps the hiding behavior:
Output:
Sales report
Report
Draft report
b and c are the same kind of object, yet they print different titles. With new, the method chosen depends on the variable's type, decided at compile time. Code that handles reports as Report (a list, a method parameter, a framework callback) never sees DraftReport.Title. That is almost never what you want. Use override for polymorphism; new exists mainly for the case where a base class you do not control adds a member whose name collides with one you already have.
sealed
sealed on a class forbids deriving from it:
sealed class Invoice { }
class CorrectedInvoice : Invoice { }
// error CS0509: 'CorrectedInvoice': cannot derive from sealed type 'Invoice'
string is sealed, as are many framework types. On an override, sealed stops the chain at that level:
class Shape { public virtual string Name() => "shape"; }
class Square : Shape { public sealed override string Name() => "square"; }
class Tile : Square { public override string Name() => "tile"; }
// error CS0239: 'Tile.Name()': cannot override inherited member 'Square.Name()' because it is sealed
Designing a class for inheritance takes work: deciding what is virtual, what derived classes may rely on, what order things happen in. A class that was not designed that way is safer sealed, and sealing can be undone later without breaking anyone, while unsealing cannot be taken back once others derive from you. Calls to members of sealed classes can also be slightly faster, because the runtime knows no override exists.
One base class, many interfaces
A C# class has exactly one base class. class Admin : User, Employee is error CS1721 ("cannot have multiple base classes"). A class can, however, implement any number of interfaces alongside its base class:
class Admin : User, IAuditable, IComparable<Admin>
{
// base class first, then interfaces, in any order
}
Use a base class for "is a kind of, and shares implementation with", and interfaces for "can do". When the base class only exists to force derived classes to fill in some methods, an abstract class is the tool.
Casting up and down the hierarchy
A derived object can always be used where its base type is expected. That upcast is implicit and cannot fail. Going the other way, a downcast, needs an explicit cast and fails at run time if the object is not of that type:
Output:
Rex fetches the ball
InvalidCastException: Tom is not a Dog
True
Rex fetches the ball
Writing Dog d = pet; without the cast is a compile error (CS0266: "An explicit conversion exists (are you missing a cast?)"), because the compiler only knows pet is some Animal. Prefer is with a variable when the object might be of another type, and a plain cast only when anything else would be a bug. Frequent downcasting is a design smell: it usually means the behavior belongs in a virtual method on the base class. The pattern matching page covers the is forms in full.
Inheritance and collections
Polymorphism works element by element, but generic collections of a derived type are not collections of the base type: List<Animal> animals = new List<Dog>(); does not compile, because the list would then accept a Cat. Read-only views are covariant, so IEnumerable<Animal> animals = new List<Dog>(); is fine. The generics page explains why.
Common mistakes
- Forgetting
override. The method compiles with a warning and silently hides instead of overriding. Treat CS0114 as an error. - Making everything
virtual. Each virtual member is a promise to derived classes about when it is called and what it may assume. Only mark the extension points you intend. - Deep hierarchies. Three or four levels of inheritance make it hard to know which version of a method runs. Composition, a class holding another object and calling it, is often simpler than a new level.
- Calling virtual methods from a constructor. The override runs before the derived constructor's body, so any field that body assigns still holds its default value.
- Using inheritance only to reuse code. If a
Stackinherits fromList, callers canInsertinto the middle of your stack. Hold aListin a private field instead.
Frequently Asked Questions
How does inheritance work in C#?
A class names one base class after a colon: class Dog : Animal. The derived class gets all of the base class's members (fields, properties, methods, events), can add its own, and can override those the base marked virtual. Constructors are not inherited, and private members, although present in the object, are not accessible from the derived class.
What is the difference between virtual and override in C#?
virtual goes on the base class method and says "derived classes may replace this". override goes on the derived class method and does the replacing. Both are needed: overriding a method that is not virtual, abstract or already override is error CS0506. When a virtual method is called, the runtime runs the version for the object's actual type, not the variable's type.
What is the difference between new and override in C#?
override replaces the base method for every caller, even code that holds the object through a base-class variable. new only hides it: code that sees the object as the derived type calls the new method, while code that sees it as the base type still calls the base method. So Base b = new Derived(); b.M(); runs Derived.M with override and Base.M with new.
Does C# support multiple inheritance?
Not for classes: a class has exactly one base class, and listing two is error CS1721. A class can implement any number of interfaces, which is how C# models "this type can do several things". Since C# 8, interfaces can also carry default method implementations.
What does sealed mean in C#?
A sealed class cannot be used as a base class; deriving from it is error CS0509. string is sealed, for example. On a method, sealed override stops classes further down the hierarchy from overriding it again (CS0239). Sealing classes that were not designed for inheritance is a reasonable default.
How do I call the base class method in C#?
Use base.MethodName(...) inside the derived class, usually inside the override: public override string Describe() => base.Describe() + " with GPS";. For constructors, use : base(...) after the parameter list to pick which base constructor runs.