ZetCode

C# abstract class

last modified October 5, 2026

In this article we show how to work with abstract classes in C#.

An abstract class is an unfinished class that must be completed in its subclasses. It is declared with the abstract keyword and may contain both fully implemented members and abstract members. The purpose of an abstract class is to provide a common definition, and often a partial implementation, for descendant classes.

Abstract classes cannot be instantiated. A class that declares at least one abstract member must itself be declared abstract. An abstract method has no body; it only declares the signature. When we derive from an abstract class, the derived class must implement every inherited abstract member with the override keyword, unless the derived class is also abstract. An override must keep the same accessibility as the abstract member it implements.

Unlike interfaces, abstract classes can declare instance fields and constructors, and they can hold state. Since C# 8, interfaces may also provide default method implementations, but they cannot store instance state. A class may implement any number of interfaces, yet it can inherit from only one base class.

C# abstract class example

The following example creates an abstract class with one abstract method and one implemented method.

Program.cs
var c = new Circle(12, 45, 22);

Console.WriteLine(c);
Console.WriteLine($"Area of circle: {c.Area():F2}");
Console.WriteLine(c.GetCoordinates());

Console.WriteLine("---------------------");

var r = new Rectangle(10, 20, 50, 60);
Console.WriteLine(r);
Console.WriteLine($"Area of rectangle: {r.Area():F2}");
Console.WriteLine(r.GetCoordinates());

abstract class Drawing
{
    protected int x;
    protected int y;

    public abstract double Area();

    public string GetCoordinates()
    {
        return $"x: {x}, y: {y}";
    }
}

class Circle : Drawing
{
    private readonly int r;

    public Circle(int x, int y, int r)
    {
        this.x = x;
        this.y = y;
        this.r = r;
    }

    public override double Area()
    {
        return r * r * Math.PI;
    }

    public override string ToString()
    {
        return $"Circle at x: {x}, y: {y}, radius: {r}";
    }
}

class Rectangle : Drawing
{
    private readonly int width;
    private readonly int height;

    public Rectangle(int x, int y, int width, int height)
    {
        this.x = x;
        this.y = y;
        this.width = width;
        this.height = height;
    }

    public override double Area()
    {
        return width * height;
    }

    public override string ToString()
    {
        return $"Rectangle at x: {x}, y: {y}, w: {width} h: {height}";
    }
}

We have an abstract base Drawing class. The class defines two member fields, declares one method, and implements another one. The Drawing class is abstract because it cannot be drawn itself; only concrete shapes such as a circle or a rectangle can be drawn. At the same time, the class provides functionality that all drawable shapes share.

The declaration abstract class Drawing uses the abstract keyword to define an abstract class. A type declared this way cannot be instantiated with new, and attempting to do so is a compile-time error.

The member public abstract double Area(); is an abstract method. It is preceded by the abstract keyword and ends with a semicolon instead of a method body, so it only specifies the signature. An abstract member is implicitly virtual and cannot be marked with virtual or static.

class Circle : Drawing makes Circle a subclass of the Drawing class. Because Circle is concrete, it must implement the inherited Area method with the override keyword. The override modifier tells the compiler that we are replacing the inherited abstract member.

The field private readonly int r; stores the radius, which is specific to the Circle class. The constructor of Circle sets all three fields, including the x and y fields inherited from Drawing. The Rectangle class follows the same pattern and provides its own implementation of the Area method.

Both derived classes also override ToString, which is inherited from object and is virtual, so they can be printed directly with Console.WriteLine. The :F2 format specifier in the interpolated strings rounds the area to two decimal places.

$ dotnet run
Circle at x: 12, y: 45, radius: 22
Area of circle: 1520.53
x: 12, y: 45
---------------------
Rectangle at x: 10, y: 20, w: 50 h: 60
Area of rectangle: 3000.00
x: 10, y: 20

Abstract properties and sealed overrides

Abstract members are not limited to methods. Properties, indexers, and events can be abstract as well. A derived class can also protect an implementation from further overriding by marking it sealed.

Program.cs
var shapes = new List<Shape> { new Square(4), new Cube(3) };

foreach (var shape in shapes)
{
    Console.WriteLine($"{shape.Name}: area = {shape.Area():F2}");
}

abstract class Shape
{
    public abstract string Name { get; }

    public abstract double Area();
}

class Square : Shape
{
    protected readonly double side;

    public Square(double side) => this.side = side;

    public override string Name => "square";

    public override double Area() => side * side;
}

class Cube : Square
{
    public Cube(double side) : base(side) { }

    public sealed override string Name => "cube";

    public override double Area() => 6 * base.Area();
}
$ dotnet run
square: area = 16.00
cube: area = 54.00

The Shape class declares an abstract property Name and an abstract method Area. Square implements both with expression-bodied members. Cube derives from Square, overrides Area to compute the surface area of a cube, and marks its Name implementation sealed. A sealed override may not be overridden again by a further derived class, and a sealed class cannot be used as a base class at all. Note that sealed is valid only together with override on a member, or on a class declaration.

Abstract class or interface?

Use an abstract class when related types should share state and a common implementation, and when a single inheritance chain makes sense. Use an interface when unrelated types only need to guarantee a contract, or when a type must expose several contracts at once. Abstract classes can define fields, constructors, and protected members; interfaces cannot hold instance state. Because C# allows only one base class, a design that needs multiple capabilities is usually expressed with interfaces plus, optionally, a shared abstract base class.

Source

abstract - C# reference
Abstract and Sealed Classes and Class Members - C# programming guide
Interfaces - C# fundamentals

In this article we worked with abstract classes in C#.

Author

Jan Bodnar writes programming tutorials and educational content.

List all C# tutorials.