This article is a quick overview for C# Interface vs Abstract Class. Object-oriented programming languages like C# offer several tools for structuring and organizing code, such as interfaces and abstract classes. Both of these concepts allow developers to define common behaviors and characteristics across different classes, improving code reusability and reducing duplication. However, while they share some similarities, they also have distinct differences that make them better suited for different situations. By the way, did you know that we offer a unique online course that boosts your C# career? Check it out here!
Similarities between interfaces and abstract classes:
- Both are used to define a contract for implementing classes, specifying the methods and properties to be implemented in order to use them.
- Neither can be instantiated directly, but must be inherited by other classes.
Differences between interfaces and abstract classes:
- Abstract classes can provide concrete implementations of methods and properties, whereas interfaces cannot. This means that classes that inherit from an abstract class can use the provided implementations, while those that implement an interface must provide their own.
- A class can inherit from multiple interfaces, but can only inherit from a single abstract class.
- Interfaces can be implemented by classes that do not share a common inheritance hierarchy, making them more flexible for certain situations.
- To demonstrate the use of interfaces and abstract classes in a real-world business application, let’s consider an example of a payroll system for a company. We can define an interface
IPayable
that specifies the behavior that must be implemented by any class that represents an employee who can be paid. This interface might include methods likeCalculatePay()
andGetPayStub()
.
Interface Example
1 2 3 4 5 |
public interface IPayable { decimal CalculatePay(); string GetPayStub(); } |
Abstract Classes
Next, we can define an abstract class Employee
that provides some common functionality that is shared by all employees, such as storing their name and employee ID. This abstract class can also implement the IPayable
interface, providing a default implementation of the GetPayStub()
method.
1 2 3 4 5 6 7 8 9 10 11 12 |
public abstract class Employee : IPayable { public string Name { get; set; } public int EmployeeID { get; set; } public virtual string GetPayStub() { throw new NotImplementedException(); } public abstract decimal CalculatePay(); } |
Concrete Classes
Finally, we can define concrete classes for each type of employee, such as SalariedEmployee
and HourlyEmployee
, which inherit from the Employee abstract class and implement the CalculatePay
method according to their own rules.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
public class SalariedEmployee : Employee { public decimal Salary { get; set; } public override decimal CalculatePay() { // Implementation to calculate salary goes here throw new NotImplementedException(); } } public class HourlyEmployee : Employee { public decimal HourlyRate { get; set; } public int HoursWorked { get; set; } public override decimal CalculatePay() { // Implementation to calculate pay based on hourly rate and hours worked goes here throw new NotImplementedException(); } } |
This example demonstrates how interfaces and abstract classes can work together to provide a consistent structure for a complex system with many different types of objects. The IPayable
interface defines the behavior that all payables must have, while the Employee
abstract class provides a common base for all employees, including a default implementation of the GetPayStub()
method. Finally, the concrete SalariedEmployee
and HourlyEmployee
classes inherit from the Employee
abstract class and provide their own implementations of the CalculatePay()
method, according to their own unique characteristics.
Implementing Multiple Interfaces
Here’s an example of how a class can implement multiple interfaces in C#:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
public interface IReadable { string Read(); } public interface IWritable { void Write(string data); } public class File : IReadable, IWritable { private string _path; public File(string path) { _path = path; } public string Read() { // Code to read data from the file return "Data read from the file."; } public void Write(string data) { // Code to write data to the file } } |
In this example, we have two interfaces: IReadable
and IWritable
. The File
class implements both of these interfaces, meaning that it must provide implementations for the Read()
and Write()
methods defined by each interface.
The File class has a private field _path
that stores the path to the file that it represents. When an instance of File
is created, the path
is passed in as a parameter to the constructor.
The Read()
method of File
reads data from the file at the given path, and returns it as a string. The Write()
method writes the given data to the file at the given path.
By implementing both IReadable
and IWritable
, the File
class can be used in a variety of different contexts where both reading and writing to a file is required.
This is how it can be used:
1 2 3 4 5 |
IReadable myFileReader = new File("C:/path/to/myfile.txt"); string data = myFileReader.Read(); IWritable myFileWriter = new File("C:/path/to/myfile.txt"); myFileWriter.Write("New data to write to the file."); |
In the first example, an instance of File
is created and assigned to a variable of type IReadable
. This variable can only be used to call the Read()
method, because that is the only method defined by IReadable
.
In the second example, another instance of File
is created and assigned to a variable of type IWritable
. This variable can only be used to call the Write()
method, because that is the only method defined by IWritable
.
C# Interface vs Abstract Class Summary
In summary, interfaces and abstract classes are both useful tools for defining common behavior across different classes. However, they have distinct differences that make them better suited for different situations. By using both together, developers can create a flexible and structured system that is easy to maintain and extend over time. If you want to skyrocket your C# career, check out our powerful ASP.NET full-stack web development course that also covers test-driven development.