For many developers, interfaces are confusing and their purpose not clearly understood. Interfaces are actually quite easy to get to grips with once you understand the concept that defines an interface.
Interfaces act like verbs. So, for example, if we had to create two classes called Lion
and Tiger
that derive from the Cat
abstract class, the interface would describe some sort of action. Lions and tigers can roar (but not purr). We can then create an interface called IRoarable
. If we had to derive a class called Cheetah
from our abstract class Cat
, we would not be able to use the IRoarable
interface, because cheetahs purr. We would need to create an IPurrable
interface.
Creating an interface is very similar to creating an abstract class. The difference is that the interface is describing what the class can do, in the case of the Cheetah
class, by implementing IPurrable
.
Cat
:public abstract class Cat { public abstract void Eat(); public abstract void Hunt(); public abstract void Sleep(); }
Cheetah
that inherits from the Cat
abstract class:public class Cheetah : Cat { }
Cat
abstract class, Visual Studio will show you a warning via the lightbulb feature. As you inherited from the abstract class Cat
, you have to implement the abstract members within the abstract class in your derived class Cheetah
:Cheetah
class:NotImplementedException
if you try to use the class as is. The reason for using an abstract class is to implement the functionality defined in the abstract class Cat
in the derived class Cheetah
. Not doing so contravenes the rules for using abstract classes:public class Cheetah : Cat { public override void Eat() { throw new NotImplementedException(); } public override void Hunt() { throw new NotImplementedException(); } public override void Sleep() { throw new NotImplementedException(); } }
Cheetah
class as follows. The implementation in the overridden methods is simple, but this validates the rule of writing some sort of implementation in the overridden methods:public class Cheetah : Cat { public override void Eat() { WriteLine($"The cheetah eats."); } public override void Hunt() { WriteLine($"The cheetah hunts."); } public override void Sleep() { WriteLine($"The cheetah sleeps."); } }
IPurrable
that will be implemented on the Cheetah
class. A common naming convention for interfaces dictates that the interface name should be prefixed with a capital I:interface IPurrable { }
SoftPurr
method contains no implementation at all. It however specifies that we will need to pass this method an integer value for the decibel that the Cheetah
class will purr at:interface IPurrable { void SoftPurr(int decibel); }
IPurrable
interface on the Cheetah
class. To do this, we need to add the IPurrable
interface name after the Cat
abstract class name. If the Cheetah
class did not inherit from the abstract class, then the interface name would simply follow after the colon:public class Cheetah : Cat, IPurrable { public override void Eat() { WriteLine($"The cheetah eats."); } public override void Hunt() { WriteLine($"The cheetah hunts."); } public override void Sleep() { WriteLine($"The cheetah sleeps."); } }
Cheetah
class implements the IPurrable
interface, Visual Studio once again displays a warning via the lightbulb feature. It is warning us that the Cheetah
class does not implement the SoftPurr
method defined in the interface IPurrable
:SoftPurr
method implicitly by selecting the first option in the lightbulb suggestion. You will see that by selecting to implement the SoftPurr
method defined in the IPurrable
interface implicitly, adds it as if it were part of the Cheetah
class:public class Cheetah : Cat, IPurrable { public void SoftPurr(int decibel) { throw new NotImplementedException(); } public override void Eat() { WriteLine($"The cheetah eats."); } public override void Hunt() { WriteLine($"The cheetah hunts."); } public override void Sleep() { WriteLine($"The cheetah sleeps."); } }
SoftPurr
method, it looks like a normal method inside the Cheetah
class. This would be fine unless our Cheetah
class already contains a property called SoftPurr
. Go ahead and add a property called SoftPurr
to your Cheetah
class: public class Cheetah : Cat, IPurrable { public int SoftPurr { get; set; } public void SoftPurr(int decibel) { throw new NotImplementedException(); } public override void Eat() { WriteLine($"The cheetah eats."); } public override void Hunt() { WriteLine($"The cheetah hunts."); } public override void Sleep() { WriteLine($"The cheetah sleeps."); } }
Cheetah
class already contains a definition for SoftPurr
:SoftPurr
method is a member of the implementation defined in the IPurrable
interface:SoftPurr
method to your Cheetah
class as follows:public class Cheetah : Cat, IPurrable { public int SoftPurr { get; set; } void IPurrable.SoftPurr(int decibel) { throw new NotImplementedException(); } public override void Eat() { WriteLine($"The cheetah eats."); } public override void Hunt() { WriteLine($"The cheetah hunts."); } public override void Sleep() { WriteLine($"The cheetah sleeps."); } }
The compiler now knows that this is an interface that is being implemented and is therefore a valid line of code.
SoftPurr
method and use the new nameof
keyword in C# 6.0, as well as the interpolated string for the output. Also, remove the SoftPurr
property added earlier:public void SoftPurr(int decibel) { WriteLine($"The {nameof(Cheetah)} purrs at {decibel} decibels."); }
Cheetah
class as follows:Cheetah cheetah = new Cheetah(); cheetah.Hunt(); cheetah.Eat(); cheetah.Sleep(); cheetah.SoftPurr(60); Console.ReadLine();
So, you might be wondering what the difference between an abstract class and an interface is. It basically comes down to where you want your implementation. If you need to share functionality between derived classes, then an abstract class is the best fit for your needs. In other words, we had specific things that were common to all cats (lions, tigers, and cheetahs) such as hunting, eating, and sleeping. This is then best used within an abstract class.
If your implementation is specific to a class or several classes (but not all classes), then your best course of action would be to use an interface. In this case, the IPurrable
interface can be applied to several classes (for example, cheetahs and domestic cats) but can't be applied to all cats (such as lions and tigers), because not all cats can purr.
Knowing this difference and where you need to place your implementation will aid you in deciding whether you need to use an abstract class or an interface.