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.
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
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:
Interfaces cannot be instantiated directly (we get a compilation error):
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.
If our derived class does not implement all the abstract
members, we get a compilation error.
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:
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:
- A single class may implement as many interfaces as they like.
- 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.
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!
Happy Coding!