Reading — step 1 of 7
Learn
C# is single-inheritance for classes (with multiple-interface support). The Microsoft C# docs treat inheritance as a core building block, but the recommendation in modern C# is the same as Effective Java: prefer composition; use inheritance only for genuine is-a relationships.
Basic inheritance
public class Animal
{
public string Name { get; set; }
public Animal(string name)
{
Name = name;
}
public virtual string Speak() => $"{Name} makes a sound";
}
public class Dog : Animal
{
public Dog(string name) : base(name) { }
public override string Speak() => $"{Name} barks";
}
var rex = new Dog("Rex");
Console.WriteLine(rex.Speak()); // Rex barks
class Dog : Animal— inheritance syntax (colon): base(name)— call the parent constructorvirtualon the parent method allows overrideoverrideon the child method actually overrides
virtual, override, new
Three keywords that often confuse:
public class Base
{
public virtual string Show() => "Base.Show";
}
public class Sub : Base
{
public override string Show() => "Sub.Show"; // proper override
}
public class Hider : Base
{
public new string Show() => "Hider.Show"; // SHADOWS, not overrides
}
Base b1 = new Sub();
Base b2 = new Hider();
Console.WriteLine(b1.Show()); // Sub.Show — virtual dispatch
Console.WriteLine(b2.Show()); // Base.Show — `new` HIDES, doesn't override
new in this context (different from object creation) hides the parent method without overriding. Almost always a mistake — the IDE warns. Use override for proper polymorphism.
abstract — must override
public abstract class Shape
{
public abstract double Area(); // no body
public string Describe() => $"Shape with area {Area()}";
}
public class Circle : Shape
{
public double Radius { get; set; }
public Circle(double r) { Radius = r; }
public override double Area() => Math.PI * Radius * Radius;
}
var c = new Circle(5);
Console.WriteLine(c.Describe());
Abstract classes can't be instantiated. Subclasses must implement all abstract members.
sealed — prevent further inheritance
public sealed class FinalDog : Dog
{
// ...
}
// public class Puppy : FinalDog { } // ERROR — sealed
Apply sealed to classes that aren't designed for further extension. C# defaults to non-sealed (unlike Kotlin's default-final). Mark sealed when there's a real reason.
protected access
public class Account
{
protected decimal Balance { get; set; }
public void Deposit(decimal amount) => Balance += amount;
}
public class SavingsAccount : Account
{
public void AddInterest(decimal rate)
{
Balance *= (1 + rate); // OK — protected accessible from subclass
}
}
protected = visible to this class AND derived classes. private = this class only. internal = same assembly.
base — explicit parent calls
public class Dog : Animal
{
public Dog(string name) : base(name) { }
public override string Speak()
{
var parent = base.Speak();
return $"{parent} (and barks)";
}
}
base.Method() calls the parent's version. Useful when extending — call parent's behavior, then add to it.
Common mistakes
- Forgetting
virtual— child's override doesn't dispatch dynamically. Methods are NON-virtual by default in C# (different from Java where everything is virtual unlessfinal). - Using
newinstead ofoverride— silent shadowing. Calls through base reference go to base. - Deep inheritance hierarchies — Effective Java's advice applies: prefer composition.
- Constructor without
: base(...)— uses parameterless base ctor; if none exists, compile error. - Mixing inheritance with structs — structs cannot inherit (only implement interfaces).
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…