404 Not Found

404 Not Found


nginx

Abstract Classes and Interfaces

Abstract Classes and Abstract Methods

Abstract classes are declared with the abstract modifier and cannot be instantiated. They can contain both abstract methods and concrete methods. Abstract methods have no method body and must be overridden in derived classes.

Example

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

    public abstract double Area();

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

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

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

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

class Rectangle : Shape
{
    public double Width { get; set; }
    public double Height { get; set; }

    public Rectangle(double w, double h)
    {
        Name = "Rectangle";
        Width = w;
        Height = h;
    }

    public override double Area()
    {
        return Width * Height;
    }
}

Circle c = new Circle(3);
Rectangle r = new Rectangle(4, 5);
c.Print();
r.Print();
▶ Try it Yourself
TEXT
Circle Area: 28.27
Rectangle Area: 20.00

💡 An abstract class is like a "half-finished template" — it specifies what subclasses must complete (abstract methods) and also provides parts that can be used directly (concrete methods).

Abstract Classes vs Regular Classes

Feature Regular Class Abstract Class
Instantiation Yes No
Abstract methods Cannot contain Can contain
Concrete methods Yes Yes
Constructor Yes Yes
Fields Yes Yes
When inherited Optionally override virtual Must override abstract
sealed modifier Yes No (contradictory)

Example

CSHARP
abstract class Logger
{
    protected string prefix;

    public Logger(string prefix)
    {
        this.prefix = prefix;
    }

    public abstract void Log(string message);

    public void LogWithTimestamp(string message)
    {
        string ts = DateTime.Now.ToString("HH:mm:ss");
        Log($"[{ts}] {message}");
    }
}

class FileLogger : Logger
{
    public FileLogger() : base("FILE") { }

    public override void Log(string message)
    {
        Console.WriteLine($"{prefix}: {message}");
    }
}

FileLogger fl = new FileLogger();
fl.Log("Service started");
fl.LogWithTimestamp("Database connected");
▶ Try it Yourself
TEXT
FILE: Service started
FILE: [14:30:00] Database connected

⚠️ Abstract classes can have constructors, but constructors cannot be abstract. Constructors are called via base() when a subclass is instantiated.

Interface Definition and Implementation

Interfaces are defined using the interface keyword and represent a contract of members. Classes that implement an interface must provide implementations for all its members. Interface members are implicitly public and cannot have access modifiers.

Example

CSHARP
interface IMovable
{
    void Move(double dx, double dy);
    double Speed { get; set; }
}

interface IDrawable
{
    void Draw();
}

class Player : IMovable, IDrawable
{
    public double X { get; set; }
    public double Y { get; set; }
    public double Speed { get; set; }

    public Player(double x, double y, double speed)
    {
        X = x;
        Y = y;
        Speed = speed;
    }

    public void Move(double dx, double dy)
    {
        X += dx * Speed;
        Y += dy * Speed;
        Console.WriteLine($"Moved to ({X:F1}, {Y:F1})");
    }

    public void Draw()
    {
        Console.WriteLine($"Drawing player @ ({X:F1}, {Y:F1})");
    }
}

Player p = new Player(0, 0, 2.0);
p.Draw();
p.Move(3, 4);
p.Draw();
▶ Try it Yourself
TEXT
Drawing player @ (0.0, 0.0)
Moved to (6.0, 8.0)
Drawing player @ (6.0, 8.0)

📌 Interface names should start with a capital I, such as IComparable, IDisposable — this is a C# naming convention.

Multiple Interface Implementation

A class can implement multiple interfaces, separated by commas. This is one way C# achieves polymorphism, compensating for the single-inheritance limitation.

Example

CSHARP
interface IWalkable
{
    void Walk();
}

interface ISwimmable
{
    void Swim();
}

interface IFlyable
{
    void Fly();
}

class Duck : IWalkable, ISwimmable, IFlyable
{
    public string Name { get; set; }

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

    public void Walk()
    {
        Console.WriteLine($"{Name} waddles along");
    }

    public void Swim()
    {
        Console.WriteLine($"{Name} swims in the water");
    }

    public void Fly()
    {
        Console.WriteLine($"{Name} flaps its wings and takes off");
    }
}

Duck d = new Duck("Donald");
d.Walk();
d.Swim();
d.Fly();

IWalkable walker = d;
walker.Walk();

ISwimmable swimmer = d;
swimmer.Swim();
▶ Try it Yourself
TEXT
Donald waddles along
Donald swims in the water
Donald flaps its wings and takes off
Donald waddles along
Donald swims in the water

💡 Variables of different interface types can only call members defined by that interface — this reflects the Interface Segregation Principle.

Interface Default Methods (C# 8)

Starting with C# 8, interfaces can contain methods with default implementations. This allows adding new members to an interface without breaking existing implementing classes.

Example

CSHARP
interface ILogger
{
    void Log(string message);

    void LogWarning(string message)
    {
        Log($"[WARN] {message}");
    }

    void LogError(string message)
    {
        Log($"[ERROR] {message}");
    }
}

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

class SimpleLogger : ILogger
{
    public void Log(string message)
    {
        Console.WriteLine($">> {message}");
    }
}

ConsoleLogger cl = new ConsoleLogger();
cl.Log("System started");
cl.LogWarning("Low memory");
cl.LogError("Connection failed");

SimpleLogger sl = new SimpleLogger();
sl.Log("System started");
sl.LogWarning("Low memory");
▶ Try it Yourself
TEXT
System started
[WARN] Low memory
[ERROR] Connection failed
>> System started
>> [WARN] Low memory

⚠️ Default methods are only available when called through the interface type. They cannot be called through a variable of the implementing class type (unless the implementing class also re-implements the method).

Abstract Class vs Interface Selection Guide

Comparison Abstract Class Interface
Inheritance/Implementation Single inheritance Multiple implementation
Member types Any Methods, properties, events, indexers
Fields Yes No (before C# 8)
Constructor Yes No
Access modifiers Any Default public
Default implementation Yes Since C# 8
Version compatibility Adding members is safe Adding members breaks implementations (before C# 8)
Design intent "What it is" (IS-A) "What it can do" (CAN-DO)

Selection guidelines:

🔥 Quick rule: Use abstract classes for shared identity, use interfaces for shared capability.

Explicit Interface Implementation

When multiple interfaces have members with the same name, or when you need to hide interface members, use explicit interface implementation. The syntax is return_type InterfaceName.MemberName. Explicitly implemented members cannot have access modifiers and can only be called through the interface type.

Example

CSHARP
interface IEnglish
{
    void Speak();
}

interface IFrench
{
    void Speak();
}

class BilingualPerson : IEnglish, IFrench
{
    void IEnglish.Speak()
    {
        Console.WriteLine("Hello!");
    }

    void IFrench.Speak()
    {
        Console.WriteLine("Bonjour!");
    }

    public void Speak()
    {
        Console.WriteLine("Hi!");
    }
}

BilingualPerson p = new BilingualPerson();
p.Speak();

IEnglish eng = p;
eng.Speak();

IFrench fr = p;
fr.Speak();
▶ Try it Yourself
TEXT
Hi!
Hello!
Bonjour!

📌 Explicitly implemented interface members are not visible on the class instance — you must cast to the corresponding interface type to call them. This is both a limitation and an encapsulation mechanism.

IComparable<T> in Practice

IComparable<T> is one of the most commonly used interfaces in .NET, used to define the natural sort order of a type. Both Array.Sort and List<T>.Sort depend on this interface.

Example

CSHARP
class Student : IComparable<Student>
{
    public string Name { get; set; }
    public int Score { get; set; }

    public Student(string name, int score)
    {
        Name = name;
        Score = score;
    }

    public int CompareTo(Student other)
    {
        if (other == null) return 1;
        return Score.CompareTo(other.Score);
    }

    public override string ToString()
    {
        return $"{Name}({Score})";
    }
}

Student[] students = new Student[]
{
    new Student("Alice", 85),
    new Student("Bob", 92),
    new Student("Charlie", 78),
    new Student("Diana", 95),
    new Student("Eve", 88)
};

Console.WriteLine("Before sorting:");
foreach (var s in students)
    Console.WriteLine(s);

Array.Sort(students);

Console.WriteLine("After sorting (by score ascending):");
foreach (var s in students)
    Console.WriteLine(s);
▶ Try it Yourself
TEXT
Before sorting:
Alice(85)
Bob(92)
Charlie(78)
Diana(95)
Eve(88)
After sorting (by score ascending):
Charlie(78)
Alice(85)
Eve(88)
Bob(92)
Diana(95)

💻 CompareTo return value convention: negative means the current object comes before the other, zero means equal, positive means the current object comes after. With IComparable<T>, custom types can directly use Array.Sort, List<T>.Sort, and other sorting features.

❓ FAQ

Q Can an abstract class implement an interface?
A Yes. An abstract class can implement an interface and may choose to implement members concretely or leave them as abstract.
Q Can an interface inherit from another interface?
A Yes. An interface can inherit from multiple interfaces, and the implementing class must implement all members across the inheritance chain.
Q Can explicit interface implementation have a public modifier?
A No. Explicit interface implementations cannot have any access modifier — they are only accessible through the interface type.
Q What is the difference between an interface default method and an abstract class virtual method?
A Interface default methods cannot access instance fields, while abstract class virtual methods can; default methods are called through the interface, while virtual methods are called through class instances.

📖 Summary

📝 Exercises

  1. Define an abstract class Vehicle with an abstract method Start() and a concrete method Stop(), then create Car and Bike subclasses to implement it
  2. Define an interface ISerializable with method string Serialize(), and an interface IDeserializable with method void Deserialize(string data), then create a class that implements both interfaces
  3. Define two interfaces IPlayable and IRecordable, both with a void Pause() method, and use explicit interface implementation to distinguish between them
  4. Create a Product class that implements IComparable<Product> to sort by price, and test with Array.Sort
  5. Add a void PrintHeader(string title) method with a default implementation to an existing IPrintable interface, and verify that old implementing classes are not affected
Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏