C# > Object-Oriented Programming (OOP) > Polymorphism > Abstract Classes and Methods

Polymorphism with Interfaces and Abstract Classes

This example demonstrates polymorphism using both interfaces and abstract classes in C#. It shows how different classes can implement the same interface or inherit from the same abstract class and exhibit different behaviors.

Code Example

This code demonstrates polymorphism using both an interface (`ISpeakable`) and an abstract class (`Animal`). The `Animal` class implements the `ISpeakable` interface and declares an abstract method `Speak()`. The `Dog` and `Cat` classes inherit from `Animal` and provide concrete implementations for `Speak()`. The `Translator` class implement the ISpeakable interface directly. The `Main` method shows how to treat objects of different classes as objects of a common interface or abstract class type.

using System;

// Interface defining a common behavior
public interface ISpeakable
{
    string Speak();
}

// Abstract class implementing the interface
public abstract class Animal : ISpeakable
{
    public string Name { get; set; }

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

    // Abstract method to be implemented by derived classes
    public abstract string Speak();

    // Virtual method
    public virtual string Describe()
    {
        return $"This is a {GetType().Name} named {Name}.";
    }
}

// Concrete class inheriting from Animal
public class Dog : Animal
{
    public Dog(string name) : base(name) { }

    public override string Speak()
    {
        return "Woof!";
    }

    public override string Describe()
    {
        return base.Describe() + " It's a good boy!";
    }
}

// Concrete class inheriting from Animal
public class Cat : Animal
{
    public Cat(string name) : base(name) { }

    public override string Speak()
    {
        return "Meow!";
    }
}

// Concrete class implementing the interface directly
public class Translator : ISpeakable
{
    private string message;

    public Translator(string message)
    {
        this.message = message;
    }

    public string Speak()
    {
        return $"Translated: {message}";
    }
}

public class Example
{
    public static void Main(string[] args)
    {
        // Polymorphism with interface
        ISpeakable dog = new Dog("Buddy");
        ISpeakable cat = new Cat("Whiskers");
        ISpeakable translator = new Translator("Hello");

        Console.WriteLine(dog.Speak()); // Output: Woof!
        Console.WriteLine(cat.Speak()); // Output: Meow!
        Console.WriteLine(translator.Speak()); // Output: Translated: Hello

        // Polymorphism with abstract class
        Animal animalDog = new Dog("Rover");
        Animal animalCat = new Cat("Mittens");

        Console.WriteLine(animalDog.Describe()); // Output: This is a Dog named Rover. It's a good boy!
        Console.WriteLine(animalCat.Describe()); // Output: This is a Cat named Mittens.
    }
}

Concepts Behind the Snippet

  • Interface: Defines a contract that classes can implement. Specifies a set of methods that a class must implement.
  • Abstract Class: A class that cannot be instantiated directly and may contain abstract methods (methods without implementation).
  • Polymorphism: The ability to treat objects of different classes as objects of a common type (interface or abstract class).

Real-Life Use Case

Consider a game development scenario where you have different types of game entities (e.g., characters, enemies, items). You can define an interface like `IInteractable` with a method `Interact()`. Each game entity can implement this interface and provide its own specific interaction logic. The game engine can then polymorphically interact with any `IInteractable` object without knowing its specific type.

Best Practices

  • Favor interfaces over abstract classes when you need to define a contract that multiple unrelated classes can implement.
  • Use abstract classes when you have a clear inheritance hierarchy and want to share some common implementation details among derived classes.
  • Design interfaces and abstract classes to be cohesive and focused on a specific responsibility.
  • Use virtual methods in abstract classes to provide default implementations that derived classes can override.

Interview Tip

Be prepared to discuss the trade-offs between interfaces and abstract classes. Interfaces provide greater flexibility in terms of multiple inheritance, while abstract classes allow you to share implementation details and enforce a specific structure on derived classes.

When to use them

Use interfaces to define a capability, or behavior, that different classes can implement. This allows you to treat these different classes in a uniform way, using polymorphism. Use abstract classes to provide a common base for a group of related classes, allowing you to share code and enforce a certain structure. Abstract classes are useful when you have some default behavior that you want to provide, but you also want to ensure that subclasses implement certain methods in their own way.

Memory footprint

Interfaces and abstract classes have different impacts on memory. Interfaces, being a contract, don't add any data or implementation directly to classes that implement them. Abstract classes, on the other hand, can include data members and method implementations, which contribute to the memory footprint of any class that inherits from them.

Alternatives

  • Delegates and Events: Provide a way to implement callback mechanisms and decouple components.
  • Composition: Achieved by composing objects together to create more complex behavior

Pros

  • Flexibility: Polymorphism allows you to write code that can work with objects of different types in a uniform way.
  • Extensibility: Interfaces and abstract classes make it easier to add new types to the system without modifying existing code.
  • Maintainability: Polymorphism can improve code maintainability by reducing code duplication and making it easier to reason about the behavior of the system.

Cons

  • Complexity: Polymorphism can add complexity to the system, especially when dealing with deep inheritance hierarchies.
  • Performance Overhead: Polymorphic calls can have a slight performance overhead compared to direct method calls.

FAQ

  • Can an interface inherit from another interface?

    Yes, an interface can inherit from one or more other interfaces. This allows you to create more complex interfaces that combine the capabilities of multiple interfaces.
  • Can an abstract class implement an interface?

    Yes, an abstract class can implement one or more interfaces. This is a common pattern for providing a default implementation for some of the interface methods while leaving others to be implemented by derived classes.
  • Is it possible to create a sealed class that implements an interface?

    Yes, a sealed class can implement an interface. A sealed class cannot be inherited from, so it must provide a complete implementation for all members of the interface. This can be useful when you want to create a final, non-extensible implementation of a particular interface.