C# Abstract Vs Interface Vs Class
An abstract class allows you to create functionality that subclasses can implement or override. An interface only allows you to define functionality, not implement it. Whereas a class can extend only one abstract class, it can take advantage of multiple interfaces.
So, if we will look at an existing CAD application and mimic how it could have been built in C#, using Interfaces and Abstract classes.
Within the IDocuments interface, we will define what the Abstract class will implement.
//IDocument.cs namespace MainLibrary { public interface IDocument { string Name { get; set; } bool Save(string iPath); bool Close(); } }
The Abstract class ADocument will implement the interface, but notice how we define the Save method as virtual. Virtual methods have an implementation and provide the derived classes with the option of overriding it. So if another class wants to override the Save method it can, otherwise it will utilize the predefined method. The Close method is defined as Abstract, Abstract methods do not provide an implementation and force the derived classes to override the method. So in a lower class, the class has to provide the code for this method.
//ADocument.cs using System; using System.IO; namespace MainLibrary { public abstract class ADocument : IDocument { public string Name { get; set; } public virtual bool Save(string iPath) { bool ReturnBool = false; try { string[] lines = { "First line", "Second line", "Third line" }; File.WriteAllLines(iPath, lines); ReturnBool = true; } catch(Exception ex) { throw new Exception("Failed to Save File.", ex); } return ReturnBool; } public abstract bool Close(); } }
Within the IPartDocument interface, we have added an additional Object that is only found in this document type.
//IPartDocument.cs namespace MainLibrary { public interface IPartDocument : IDocument { IPart Part { get; set; } } }
Within the PartDocument class, we can see that we have to provide code for the Abstract method close, we also see that we have the additional object from the IPartDocument interface. What we don’t see are the property’s and methods inherited from the abstract object, but hey are there.
//PartDocument.cs using System; namespace MainLibrary { public class PartDocument : ADocument , IPartDocument { public IPart Part { get; set; } public override bool Close() { bool ReturnBool = false; Console.WriteLine("Closing Document."); return ReturnBool; } } }
And we can see that, PartDocument has Close, Name, Part and Save Methods and Property’s available.
We can now define other Document Types, in the same way, that would inherit from the abstract object and be forced to override specific methods or property’s.
If we make the Name property abstract in the Abstract class as shown below;
using System; using System.IO; namespace MainLibrary { public abstract class ADocument : IDocument { public abstract string Name { get; set; } public virtual bool Save(string iPath) { bool ReturnBool = false; try { string[] lines = { "First line", "Second line", "Third line" }; File.WriteAllLines(iPath, lines); ReturnBool = true; } catch(Exception ex) { throw new Exception("Failed to Save File.", ex); } return ReturnBool; } public abstract bool Close(); } }
We would be forced to implement the Name property in all the document sub classes.
using System; namespace MainLibrary { public class PartDocument : ADocument , IPartDocument { public IPart Part { get; set; } public override string Name { get; set; } public override bool Close() { bool ReturnBool = false; Console.WriteLine("Closing Document."); return ReturnBool; } } }
So let’s add one more document sub class, first let’s create a new object to help make the document sub class unique.
//IProduct.cs namespace MainLibrary { public interface IProduct { } }
//Product.cs namespace MainLibrary { public class Product : IProduct { } }
Now we will create the new document sub class.
//IProductDocument.cs namespace MainLibrary { public interface IProductDocument : IDocument { IProduct Product { get; set; } } }
//ProductDocument.cs using System; namespace MainLibrary { public class ProductDocument : ADocument, IProductDocument { public IProduct Product { get; set; } public override bool Close() { throw new NotImplementedException(); } } }
Just as before we can see that the Product Document has Close, Name, Part and Save Methods and Property’s available and the unique object product.
So this is what we have, the same methodology can be applied anywhere where we need an abstract object.