In the previous post, we discussed various ways of implementing inheritance in C# classes. In this one, we're going to discuss two ways a class can inherit a specific set of functionality without inheriting an entire class.

An abstract painting, using deep blacks and reds, with no obvious pattern.
If you cross your eyes and squint, you might get a headache. Photo by Cassi Josh / Unsplash

First, we'll discuss a way to allowing class to implement a common set of property and method definitions (interfaces), and then we'll discuss a manner by which a class can be defined but only partially implemented so that its derived classes can use their own implementation (abstract classes). We will also point out some important difference between interfaces and abstract classes along the way.

The Sample Project

exceptionnotfound/CSharpInSimpleTerms
Contribute to exceptionnotfound/CSharpInSimpleTerms development by creating an account on GitHub.
Projects for this post: 10Interfaces and 10AbstractClasses

Interfaces

Interfaces are special objects in C# that defines a set of related functionalities which may include methods, properties, and other members. Think of interfaces as a contract, one where classes that implement an interface agree to provide implementations for all objects defined by that interface.

Interfaces cannot contain any implementations, and their names are generally prefixed with "I" to distinguish them from other C# objects. We create interfaces using the interface keyword:

public interface IAreaCalculator
{
    double GetArea();
}

Classes and structs can then implement an interface and define the behavior of the interface's methods:

public class Circle : IAreaCalculator
{
    public double Radius { get; set; }
    
    public double GetArea()
    {
        return Math.PI * (Radius * Radius);
    }
}

public class Rectangle : IAreaCalculator
{
    public double Height { get; set; }
    public double Width { get; set; }
    
    public double GetArea()
    {
        return Height * Width;
    }
}

public class Triangle : IAreaCalculator
{
    public double Height { get; set; }
    public double Width { get; set; }
    
    public double GetArea()
    {
        return Height * Width * 0.5;
    }
}

Note that the implementing class must provide a definition for all methods defined on the interface. If it does not, we get a compilation error:

public class Oval : IAreaCalculator
{
    public double Radius1 { get; set; }
    public double Radius2 { get; set; }
}
Error: "Oval" does not implement interface member "IAreaCalculator.GetArea()"

Interfaces cannot be instantiated directly (we get a compilation error):

var myAreaCalculator = new IAreaCalculator();
Error: Cannot create an instance of the abstract class or interface 'IAreaCalculator'

However, because of polymorphism, we can use interfaces as the type of a variable, and the resulting object will only have the members of the interface be usable:

IAreaCalculator myCalc = new Circle()
{
    Radius = 2
}
double area = myCalc.GetArea(); //12.566

Interface Inheritance

Interfaces can inherit from one or more other interfaces:

public interface IMovement
{
    public void Move();
}

public interface IMakeSound
{
    public void MakeSound();
}

public interface IAnimal : IMovement, IMakeSound
{
    string SpeciesName { get; set; }
}

Any class or struct which implements an interface must implement all methods and properties from any of that interface's inherited interfaces:

public class Dog : IAnimal
{
    public string SpeciesName { get; set; }

    public void MakeSound() //Defined in IMakeSound
    {
        Console.WriteLine("Bark!");
    }

    public void Move() //Defined in IMovement
    {
        Console.WriteLine("Running happily!");
    }
}

Implementing Multiple Interfaces

Likewise, a single class can implement multiple interfaces, and must define behavior for all methods and properties from those interfaces:

public interface IMovement
{
    public void Move();
}

public interface IMakeSound
{
    public void MakeSound();
}

public interface IAnimal2
{
    string SpeciesName { get; set; }
}

public class Cat : IMovement, IMakeSound, IAnimal2
{
    public string SpeciesName { get; set; }

    public void MakeSound() 
    {
        Console.WriteLine("Meow!");
    }

    public void Move() 
    {
        Console.WriteLine("Walking gracefully");
    }
}

In short, interfaces allow us to define a set of properties and method definitions which any implementing classes must provide their own implementations for.

Abstract Classes

Abstract classes serve a slightly different purpose than interfaces. An abstract class is a "partially implemented" class which other classes can inherit from, but if they do, they must provide their own implementations for any method in the abstract class that is not already implemented.

An abstract class is defined using the abstract keyword. This keyword tells us that the object being modified has a missing or incomplete implementation, and that classes which inherit from the abstract class must provide the missing pieces of the implementation.

public abstract class Basic
{
    //Members go here
}

Abstract Methods

Methods in an abstract class may also be declared abstract; these methods will not have an implementation. However, abstract classes can also contain methods with an implementation.

public abstract class Gem
{
    public abstract decimal GetValuePerCarat(); //Abstract method

    public string GetCommonColors() //Concrete method
    {
        return "Clear, Purple, Red, Black, White";
    }
}

Methods which are declared as abstract must be implemented in any derived class that inherits from the abstract class.

public class Garnet : Gem
{
    public override decimal GetValuePerCarat()
    {
        return 500M;
    }
}

public class Amethyst : Gem
{
    public override decimal GetValuePerCarat()
    {
        return 300M;
    }
}

public class Pearl : Gem
{
    public override decimal GetValuePerCarat()
    {
        return 400M;
    }
}
Extra special bonus points to whoever knows what this is from.

If our derived class does not implement all the abstract members, we get a compilation error.

public class Peridot : Gem { }
Error: 'Peridot' does not implement inherited abstract member 'GetValuePerCarat()'

An instance of the derived class may call methods defined in the derived class as well as any non-abstract methods in the base class:

var garnet = new Garnet();
var value = garnet.GetValuePerCarat(); //Defined in Garnet class
Console.WriteLine(value);
var commonColors = garnet.GetCommonColors(); //Defined in Gem base class
Console.WriteLine(commonColors);

Abstract Properties

Properties can also be declared abstract, and derived classes can implement them using the override keyword:

public abstract class Mineral 
{
    public abstract double Hardness { get; }
}

public class Quartz : Mineral
{
    public override double Hardness
    {
        get { return 7.0; }
    }
}

public class Bismuth : Mineral
{
    public override double Hardness 
    {
        get { return 2.25; }
    }
}

Other Details

Like an interface, an abstract class cannot be instantiated directly:

var myMineral = new Mineral(); //BUILD ERROR!
Error: Cannot create an instance of the abstract class or interface "Mineral".

Also, like an interface, an abstract class can be used as a variable type due to polymorphism. Methods which are defined in the derived classes will still be called when invoked.

Mineral myMineral = new Bismuth();
var valuePP = myMineral.GetValuePerPound();

Implementing and Inheriting

There are two basic rules to follow when trying to implement interfaces and inherit from classes:

  1. A single class may implement as many interfaces as they like.
  2. A single class may only inherit from one other class.

You might recall from the previous part of this series that C# does not permit multiple inheritance.

To see this demonstrated, let's consider the following interfaces and abstract class:

public interface IValuable
{
    decimal Value { get; set; }
}

public interface IHardness
{
    double Hardness { get; set; }
}

public abstract class Gem2
{
    public abstract string Color { get; set; }
}

Using these objects, we could implement the interfaces and inherit from the abstract class to build other classes:

public class LapisLazuli : Gem2, IHardness, IValuable
{
    public decimal Value { get; set; }
    public double Hardness { get; set; }

    public override string Color { get; set; }

    public LapisLazuli()
    {
        Value = 80M;
        Hardness = 5.5;
        Color = "Deep, ocean blue";
    }
}

//We've decided that Silica is not a valuable mineral, 
//so it does not implement IValuable
public class Silica : Gem2, IHardness
{
    public double Hardness { get; set; }

    public override string Color { get; set; }

    public Silica()
    {
        Hardness = 7.0;
        Color = "Various";
    }
}

Glossary

  • Interface - A "contract" which classes can implement and thereby agree to create implementations for each of the interface's members.
  • Abstract class - A base class for which the implementation is either missing or only partially complete. This class can be inherited and may optionally have its members overridden.
  • Implement - Classes which use a particular interface are said to be implementing that interface.

New Keywords

  • interface - Identifies a new C# interface object. Objects which implement an interface must provide implementation for all of the interface's members.
  • abstract - Identifies a class with a partial or missing implementation. Inheriting classes must provide the remainder of the implementation.

An Important Note!

Pretty much everything in this article is correct, until C# 8.0. At that point, the distinction between how interfaces behave and how abstract classes behave becomes much more unclear. For more details, check out Jeremy Bytes's blog post below.

Abstract Classes vs. Interfaces in C# - What You Know is Probably Wrong
A little over a year ago, C# 8 changed a lot of things about interfaces. One effect is that the technical line between abstract classes and ...

Summary

Interfaces create a contract, a collection of methods, properties, and other members that can be implemented by classes and structs. Classes which implement an interface must define all properties and method specified by the interface.

Abstract classes can define both concrete members which have a default implementation, and abstract members which must then be implemented by derived classes. Concrete members can be overridden with a different implementation in derived classes.

Both interfaces and abstract classes cannot be instantiated, but due to polymorphism, an instance of a class which either implements an interface or inherits from an abstract class can be treated as though it is said interface or abstract class.

In the next post in this series, we begin learning how to organize our classes, methods, objects and other code elements using namespaces. Check it out at the bookmark below!

C# in Simple Terms - Namespaces
Let’s organize our C# code using namespaces!

Happy Coding!