At the end of the previous lesson a problem was left pending: Book already has Title, Author, Available, Lend(), Return() and ShowDetails() perfectly defined, but if BiblioTech wanted to add magazines to the catalog too, you'd have to create a Magazine class by copying and pasting those exact same properties and methods, changing only what's specific to a magazine (for example, its issue number). That duplication is exactly what inheritance avoids: it lets you define a base class with what several types have in common, and have other classes inherit those properties and methods automatically, adding only what sets them apart. In this lesson you'll extract a base class, LibraryItem, from Book, from which both Book and a new Magazine class will inherit.

Contents

  1. The problem: duplicated properties and methods
  2. Inheriting with : — the LibraryItem base class
  3. Book inherits from LibraryItem
  4. The base operator for calling the base class constructor
  5. Adding Magazine as a second inheriting class
  6. Overriding inherited members with virtual and override
  7. Sealed classes with sealed
  8. Single inheritance hierarchy in C#

  1. The problem: duplicated properties and methods

Recall the Book class as it stood at the end of the previous lesson:

class Book
{
    public string Title { get; set; }
    public string Author { get; set; }
    public string Isbn { get; set; }
    public bool Available { get; set; } = true;

    public Book(string title, string author, string isbn)
    {
        Title = title;
        Author = author;
        Isbn = isbn;
    }

    public void Lend() { /* ... */ }
    public void Return() { /* ... */ }
    public void ShowDetails() { /* ... */ }
}

If you now wrote a Magazine class from scratch, with Title, Author, Available, Lend(), Return() and ShowDetails() practically identical, and only Isbn replaced by, say, IssueNumber, you'd be duplicating a considerable amount of code. And worse: if you later fixed a bug in Lend(), you'd have to remember to fix it in the Magazine copy too. Inheritance solves this problem at its root.

  1. Inheriting with : — the LibraryItem base class

The first step is identifying what a book and a magazine have in common within BiblioTech: both have a title, an author, and can be either available or lent out. That common part is extracted into a new class, LibraryItem, which will act as the base class:

class LibraryItem
{
    public string Title { get; set; }
    public string Author { get; set; }
    public bool Available { get; set; } = true;

    public LibraryItem(string title, string author)
    {
        Title = title;
        Author = author;
    }

    public void Lend()
    {
        if (Available)
        {
            Available = false;
            Console.WriteLine($"'{Title}' has been lent out.");
        }
        else
        {
            Console.WriteLine($"'{Title}' is not available for loan.");
        }
    }

    public void Return()
    {
        Available = true;
        Console.WriteLine($"'{Title}' has been returned.");
    }

    public void ShowDetails()
    {
        Console.WriteLine($"Title: {Title}");
        Console.WriteLine($"Author: {Author}");
        Console.WriteLine($"Available: {Available}");
    }
}

LibraryItem isn't a concept meant to be used "on its own" in BiblioTech — you'll never create an object that is "a library item" and nothing more — but rather the base on which more concrete concepts like Book or Magazine are built. This idea will come up again in the Abstraction lesson, later in the module.

  1. Book inherits from LibraryItem

For a class to inherit from another, you write a colon (:) after the class name, followed by the base class name:

class Book : LibraryItem
{
    public string Isbn { get; set; }

    public Book(string title, string author, string isbn) : base(title, author)
    {
        Isbn = isbn;
    }
}

class Book : LibraryItem means "Book is a LibraryItem, in addition to having its own particular features." Thanks to inheritance, Book automatically gets all the properties (Title, Author, Available) and methods (Lend(), Return(), ShowDetails()) from LibraryItem, without rewriting them, and only needs to declare what's specific to it: Isbn.

Book book1 = new Book("Hopscotch", "Julio Cortazar", "978-84-376-0495-4");

Console.WriteLine(book1.Title);     // Hopscotch (inherited from LibraryItem)
Console.WriteLine(book1.Isbn);       // 978-84-376-0495-4 (own to Book)
book1.Lend();                     // 'Hopscotch' has been lent out. (inherited)
book1.ShowDetails();                // inherited, shows Title/Author/Available

  1. The base operator for calling the base class constructor

In the Book constructor from the previous section, : base(title, author) appears after the constructor's signature. The base keyword lets you explicitly invoke a member of the base class — in this case, its constructor — from the inheriting class.

public Book(string title, string author, string isbn) : base(title, author)
{
    Isbn = isbn;
}

This is necessary because LibraryItem requires title and author in its own constructor (it doesn't have a parameterless constructor), so any class that inherits from it must explicitly state how that inherited part gets built. The execution order is always: first the base class constructor runs (LibraryItem(title, author), which assigns Title and Author), and then the body of Book's constructor (which assigns Isbn).

base can also be used inside a method to invoke the base class's version of that same method (you'll see this in section 6, with ShowDetails()), not just in constructors.

  1. Adding Magazine as a second inheriting class

With LibraryItem already defined, adding Magazine to BiblioTech's catalog is now much simpler: all that's needed is what sets it apart from a book, which in this case is its issue number.

class Magazine : LibraryItem
{
    public int IssueNumber { get; set; }

    public Magazine(string title, string author, int issueNumber) : base(title, author)
    {
        IssueNumber = issueNumber;
    }
}
Magazine magazine1 = new Magazine("National Geographic", "Various authors", 302);

magazine1.ShowDetails();  // inherited: Title, Author, Available
Console.WriteLine(magazine1.IssueNumber); // 302 (own to Magazine)
magazine1.Lend();       // inherited, works the same as in Book

Book and Magazine share all the logic for Title, Author, Available, Lend() and Return() without having written it twice: both inherit from the same base class LibraryItem, and each adds only its own particular feature (Isbn in one case, IssueNumber in the other).

  1. Overriding inherited members with virtual and override

By default, an inherited method behaves exactly the same in the inheriting class as in the base class: book1.ShowDetails() and magazine1.ShowDetails() literally run the same code from LibraryItem. But it makes sense for a book's details to also include its ISBN, and a magazine's, its issue number — information LibraryItem doesn't know about. To let an inheriting class override the behavior of an inherited method, the base class must mark that method as virtual, and the inheriting class must mark its own version as override:

class LibraryItem
{
    // ... properties and constructor as before ...

    public virtual void ShowDetails()
    {
        Console.WriteLine($"Title: {Title}");
        Console.WriteLine($"Author: {Author}");
        Console.WriteLine($"Available: {Available}");
    }
}

class Book : LibraryItem
{
    public string Isbn { get; set; }

    public Book(string title, string author, string isbn) : base(title, author)
    {
        Isbn = isbn;
    }

    public override void ShowDetails()
    {
        base.ShowDetails();               // runs the LibraryItem version first
        Console.WriteLine($"ISBN: {Isbn}"); // and adds Book's own line
    }
}
Book book1 = new Book("Hopscotch", "Julio Cortazar", "978-84-376-0495-4");
book1.ShowDetails();
// Title: Hopscotch
// Author: Julio Cortazar
// Available: True
// ISBN: 978-84-376-0495-4

Inside Book.ShowDetails(), the call base.ShowDetails() runs the original version of the method defined in LibraryItem (the first three lines), and then the line specific to the ISBN is added. This avoids duplicating the Title, Author and Available lines: the base class's implementation is reused, and only what's new is added. Magazine would do exactly the same with its own IssueNumber:

class Magazine : LibraryItem
{
    public int IssueNumber { get; set; }

    public Magazine(string title, string author, int issueNumber) : base(title, author)
    {
        IssueNumber = issueNumber;
    }

    public override void ShowDetails()
    {
        base.ShowDetails();
        Console.WriteLine($"Issue number: {IssueNumber}");
    }
}
Keyword Where it's used What it means
virtual On the base class's method "Inheriting classes may override this method"
override On the inheriting class's method "This is a class's own version of a method marked virtual in the base"
base.Method() Inside an overridden method "Also run the original version of the method, defined in the base class"

This lesson has focused on the mechanics of inheriting and overriding; in the next lesson, Polymorphism, you'll see why this ability to override methods is much more powerful than it seems at first glance, when combined with lists and arrays of the base class.

  1. Sealed classes with sealed

Sometimes you want to explicitly prevent a class from having any inheritors, for example because its design isn't meant to be extended further. For that, use the sealed keyword before class:

sealed class Magazine : LibraryItem
{
    public int IssueNumber { get; set; }

    public Magazine(string title, string author, int issueNumber) : base(title, author)
    {
        IssueNumber = issueNumber;
    }

    public override void ShowDetails()
    {
        base.ShowDetails();
        Console.WriteLine($"Issue number: {IssueNumber}");
    }
}

// class DigitalMagazine : Magazine { }
// Compilation error: cannot inherit from a sealed class

Marking Magazine as sealed makes it clear, for anyone reading the code, that it isn't meant to be a new base class: it's an end point in that branch of the hierarchy. LibraryItem and Book, on the other hand, remain without sealed for the rest of the module, since LibraryItem needs to stay inheritable by design.

  1. Single inheritance hierarchy in C#

An important — and deliberate — limitation of C# is that a class can only inherit from one base class: there's no multiple inheritance of classes, unlike other aspects of the language (such as interfaces, which can indeed be combined several at a time, and which you'll see in Module 4). This means BiblioTech's inheritance hierarchy always forms a tree, never a network:

classDiagram
    LibraryItem <|-- Book
    LibraryItem <|-- Magazine
    class LibraryItem {
        +string Title
        +string Author
        +bool Available
        +Lend()
        +Return()
        +ShowDetails()
    }
    class Book {
        +string Isbn
        +ShowDetails()
    }
    class Magazine {
        +int IssueNumber
        +ShowDetails()
    }

All classes in C# — including LibraryItem, even though you didn't write it explicitly — ultimately inherit from a common root class called object, from which methods like ToString() come, which you've already used indirectly when interpolating objects into strings. This is beyond the scope of this lesson, but it's worth knowing that C#'s class hierarchy always has that common root at the top.

Common Mistakes and Tips

  • Forgetting base(...) when the base class has no parameterless constructor: if LibraryItem only defines LibraryItem(string title, string author), any inheriting class is required to state : base(...) with those arguments; if not, the compiler gives an error.
  • Overriding without override (or without virtual in the base): if the base class's method isn't virtual, the inheriting class can't use override; and if instead you try to "hide" the method with a different name unrelated to real overriding (using new in the method's signature), the resulting behavior tends to be confusing and rarely what was intended. Always use virtual/override when the intent is to override.
  • Duplicating code that's already in the base class: if Book and Magazine redefine Title, Author or Available on their own, the advantage of inheritance is lost and the problem this lesson solves is reintroduced.
  • Marking a class sealed that you actually need to keep extending: sealed is a deliberate design decision; apply it only when you're certain that class shouldn't have any more inheritors (like Magazine in this module), not out of habit.
  • Expecting multiple inheritance of classes: in C# a class can only inherit from a single base class; if you need to combine several independent capabilities, the right tool is interfaces, which you'll see in Module 4.

Exercises

  1. Define the LibraryItem class with the properties Title and Author (string) and Available (bool, with a property initializer set to true), a constructor LibraryItem(string title, string author), and a method void ShowDetails() that prints the three properties to the console.

  2. Create the Book : LibraryItem class with the additional property Isbn (string) and a constructor Book(string title, string author, string isbn) that uses base(...) to initialize the inherited part. Create a Book object and print its Title (inherited) and its Isbn (own).

  3. Create the Magazine : LibraryItem class (without sealed for now) with the property IssueNumber (int) and its own constructor with base(...). Mark ShowDetails() as virtual in LibraryItem and override it in Magazine with override, so that, besides the inherited details, it also shows the issue number using base.ShowDetails().

Solutions

class LibraryItem
{
    public string Title { get; set; }
    public string Author { get; set; }
    public bool Available { get; set; } = true;

    public LibraryItem(string title, string author)
    {
        Title = title;
        Author = author;
    }

    public void ShowDetails()
    {
        Console.WriteLine($"Title: {Title}");
        Console.WriteLine($"Author: {Author}");
        Console.WriteLine($"Available: {Available}");
    }
}
class Book : LibraryItem
{
    public string Isbn { get; set; }

    public Book(string title, string author, string isbn) : base(title, author)
    {
        Isbn = isbn;
    }
}

Book book1 = new Book("Hopscotch", "Julio Cortazar", "978-84-376-0495-4");
Console.WriteLine(book1.Title); // Hopscotch
Console.WriteLine(book1.Isbn);   // 978-84-376-0495-4
class LibraryItem
{
    public string Title { get; set; }
    public string Author { get; set; }
    public bool Available { get; set; } = true;

    public LibraryItem(string title, string author)
    {
        Title = title;
        Author = author;
    }

    public virtual void ShowDetails()
    {
        Console.WriteLine($"Title: {Title}");
        Console.WriteLine($"Author: {Author}");
        Console.WriteLine($"Available: {Available}");
    }
}

class Magazine : LibraryItem
{
    public int IssueNumber { get; set; }

    public Magazine(string title, string author, int issueNumber) : base(title, author)
    {
        IssueNumber = issueNumber;
    }

    public override void ShowDetails()
    {
        base.ShowDetails();
        Console.WriteLine($"Issue number: {IssueNumber}");
    }
}

Magazine magazine1 = new Magazine("National Geographic", "Various authors", 302);
magazine1.ShowDetails();
// Title: National Geographic
// Author: Various authors
// Available: True
// Issue number: 302

Conclusion

In this lesson you've learned to avoid code duplication by extracting a base class, LibraryItem, from which Book and the new Magazine inherit via :; you've used base to invoke the base class's constructor (and its method), you've overridden inherited members with virtual/override, you've seen how sealed prevents a class from having inheritors, and why C# only allows inheriting from a single base class at a time. BiblioTech's domain model now has a real hierarchy: LibraryItem at the base, with Book and Magazine as concrete specializations.

However, you haven't yet tapped into the real power of this hierarchy: being able to treat a Book and a Magazine uniformly, as if both were simply "a LibraryItem," and letting each one behave according to its own type at runtime. That's exactly polymorphism, the topic of the next lesson.

© Copyright 2026. All rights reserved