Earn More by Sharing What You Love
Do you create content for developers working with .NET, C#, Java, Python, or Node.js? Turn your expertise into extra income!

Tim Corey
49m 41s
Inheritance and interfaces are integral parts of object-oriented programming (OOP). Tim Corey, in his video "Inheritance vs Interfaces in C#: Object Oriented Programming," provides a detailed explanation of when to use inheritance and when to opt for interfaces.
This article serves as a comprehensive guide to Tim Corey's video. It breaks down the key concepts, examples, and code explanations provided in the video, highlighting the differences between inheritance and interfaces and when to use each.
Tim at (0:00) begins by highlighting the importance of distinguishing between inheritance and interfaces. He emphasizes the need to understand when to use each concept to achieve the best results. His objective is to demonstrate this through examples, starting with incorrect usage of single inheritance and then correcting it.
At (1:08), Tim creates a simple console application using .NET 5. He names the project "OODemoApp" and explains that the primary goal is to demonstrate the concepts rather than create production-ready code.
Tim at (1:55) delves into the basics of inheritance. He defines inheritance as a mechanism where a base class's properties and methods are inherited by a derived class. He stresses that inheritance should not be used merely for code reuse and sharing but for establishing a logical "is-a" relationship.
Key points:
Tim at (7:52) creates a RentalCar class to illustrate the fundamental concept of inheritance. This class represents a rental car in a rental agency in Miami, Florida.
public class RentalCar
{
public int RentalId { get; set; }
public string CurrentRenter { get; set; }
public decimal PricePerDay { get; set; }
public int NumberOfPassengers { get; set; }
public void StartEngine()
{
Console.WriteLine("Turn key to ignition setting");
Console.WriteLine("Turn key to on");
}
public void StopEngine()
{
Console.WriteLine("Turn key to off");
}
}Public Class RentalCar
Public Property RentalId As Integer
Public Property CurrentRenter As String
Public Property PricePerDay As Decimal
Public Property NumberOfPassengers As Integer
Public Sub StartEngine()
Console.WriteLine("Turn key to ignition setting")
Console.WriteLine("Turn key to on")
End Sub
Public Sub StopEngine()
Console.WriteLine("Turn key to off")
End Sub
End ClassTim at (10:15) explains how improper use of inheritance can lead to issues. He highlights that if inheritance is misused, it can lead to code that is difficult to manage and extend. He advises against using inheritance just to share code.
Tim at (10:45) adds an enumeration for car types. He creates a new class file named Enums.cs to keep all enums in one place. This enum will help differentiate between different car styles.
// Enums.cs
public enum CarType
{
Hatchback,
Sedan,
Compact
}' Enums.vb
Public Enum CarType
Hatchback
Sedan
Compact
End EnumHe then adds a property to the RentalCar class to specify the car type.
public class RentalCar : RentalVehicle
{
public CarType Style { get; set; }
// Other properties and methods
}Public Class RentalCar
Inherits RentalVehicle
Public Property Style As CarType
' Other properties and methods
End ClassAs Tim at (12:27) explains, the rental agency decides to add trucks to their fleet, which introduces new requirements. He creates a RentalTruck class inheriting from the parent class RentalVehicle.
public class RentalTruck : RentalVehicle
{
public TruckType Style { get; set; }
// Other properties and methods
}Public Class RentalTruck
Inherits RentalVehicle
Public Property Style As TruckType
' Other properties and methods
End ClassHe then defines a new enum for truck types.
// Enums.cs
public enum TruckType
{
ShortBed,
LongBed
}' Enums.vb
Public Enum TruckType
ShortBed
LongBed
End EnumTim at (15:28) emphasizes that just because two properties share the same name doesn't mean they are the same. He illustrates this with the Style property, which could mean different enums (CarType for cars and TruckType for trucks).
The rental agency expands its fleet to include boats. Tim demonstrates how to handle this by creating a RentalBoat class. Initially, it seems manageable since boats can share some properties with cars and trucks.
public class RentalBoat : RentalVehicle
{
// Properties and methods specific to boats
}Public Class RentalBoat
Inherits RentalVehicle
' Properties and methods specific to boats
End ClassThe introduction of sailboats presents a challenge since sailboats do not have engines. Tim at (19:57) illustrates the limitations of inheritance in this scenario.
public class RentalSailboat : RentalVehicle
{
public override void StartEngine()
{
throw new NotImplementedException("I do not have an engine to start");
}
public override void StopEngine()
{
throw new NotImplementedException("I do not have an engine to stop");
}
}Public Class RentalSailboat
Inherits RentalVehicle
Public Overrides Sub StartEngine()
Throw New NotImplementedException("I do not have an engine to start")
End Sub
Public Overrides Sub StopEngine()
Throw New NotImplementedException("I do not have an engine to stop")
End Sub
End ClassTim suggests making the StartEngine and StopEngine methods virtual in the base class to allow for overriding in derived classes that do not use these methods.
public abstract class RentalVehicle
{
// Common properties
public virtual void StartEngine()
{
Console.WriteLine("Engine started");
}
public virtual void StopEngine()
{
Console.WriteLine("Engine stopped");
}
}Public MustInherit Class RentalVehicle
' Common properties
Public Overridable Sub StartEngine()
Console.WriteLine("Engine started")
End Sub
Public Overridable Sub StopEngine()
Console.WriteLine("Engine stopped")
End Sub
End ClassTim at (21:56) explains the pitfalls of having methods in inherited classes that should not be called. For the example of the RentalSailboat class, which does not have an engine, it inherits the StartEngine and StopEngine methods from the RentalVehicle class. This situation can lead to problems if these methods are called unintentionally, as they must throw exceptions to indicate that they are not applicable.
public class RentalSailboat : RentalVehicle
{
public override void StartEngine()
{
throw new NotImplementedException("I do not have an engine to start");
}
public override void StopEngine()
{
throw new NotImplementedException("I do not have an engine to stop");
}
}Public Class RentalSailboat
Inherits RentalVehicle
Public Overrides Sub StartEngine()
Throw New NotImplementedException("I do not have an engine to start")
End Sub
Public Overrides Sub StopEngine()
Throw New NotImplementedException("I do not have an engine to stop")
End Sub
End ClassTim at (24:06) emphasizes how inheritance can lead to a convoluted and messy codebase when it no longer makes logical sense. For instance, a sailboat should not be treated as a RentalVehicle with an engine. This demonstrates the limitations of inheritance and the necessity for a better design approach.
To address these issues, Tim suggests a better design using interfaces. He starts by creating a new console application project named "BetterOODemo" to demonstrate the improved approach.
Tim introduces the IRental interface to encapsulate properties common to all rentals.
public interface IRental
{
int RentalId { get; set; }
string CurrentRenter { get; set; }
decimal PricePerDay { get; set; }
}Public Interface IRental
Property RentalId As Integer
Property CurrentRenter As String
Property PricePerDay As Decimal
End InterfaceTim then creates a base class for land vehicles, separating the concept of vehicle rental from the vehicle itself.
public abstract class LandVehicle
{
public int NumberOfPassengers { get; set; }
public virtual void StartEngine()
{
Console.WriteLine("Engine started");
}
public virtual void StopEngine()
{
Console.WriteLine("Engine stopped");
}
}Public MustInherit Class LandVehicle
Public Property NumberOfPassengers As Integer
Public Overridable Sub StartEngine()
Console.WriteLine("Engine started")
End Sub
Public Overridable Sub StopEngine()
Console.WriteLine("Engine stopped")
End Sub
End ClassBy renaming the base vehicle class to LandVehicle, Tim ensures that only appropriate vehicles inherit the engine-related methods.
Tim creates Car and Truck classes that inherit from LandVehicle and implement the IRental interface.
public class Car : LandVehicle, IRental
{
public int RentalId { get; set; }
public string CurrentRenter { get; set; }
public decimal PricePerDay { get; set; }
public CarType Style { get; set; }
}
public class Truck : LandVehicle, IRental
{
public int RentalId { get; set; }
public string CurrentRenter { get; set; }
public decimal PricePerDay { get; set; }
public TruckType Style { get; set; }
}Public Class Car
Inherits LandVehicle
Implements IRental
Public Property RentalId As Integer Implements IRental.RentalId
Public Property CurrentRenter As String Implements IRental.CurrentRenter
Public Property PricePerDay As Decimal Implements IRental.PricePerDay
Public Property Style As CarType
End Class
Public Class Truck
Inherits LandVehicle
Implements IRental
Public Property RentalId As Integer Implements IRental.RentalId
Public Property CurrentRenter As String Implements IRental.CurrentRenter
Public Property PricePerDay As Decimal Implements IRental.PricePerDay
Public Property Style As TruckType
End ClassThis design maintains a clear separation of concerns, ensuring that each class only has properties and methods relevant to its type.
Tim at (31:41) discusses the importance of avoiding unnecessary code duplication. He explains that while the IRental interface requires the same properties in multiple classes, this is not considered a violation of the DRY (Don't Repeat Yourself) principle because no logic is duplicated - only property declarations.
Tim at (35:09) explains how to handle the RentalSailboat class separately by implementing the IRental interface, instead of inheriting from LandVehicle. This approach helps to avoid the pitfalls associated with inappropriate inheritance.
public class Sailboat : IRental
{
public int RentalId { get; set; }
public string CurrentRenter { get; set; }
public decimal PricePerDay { get; set; }
// Additional properties and methods specific to sailboats
}Public Class Sailboat
Implements IRental
Public Property RentalId As Integer
Public Property CurrentRenter As String
Public Property PricePerDay As Decimal
' Additional properties and methods specific to sailboats
End ClassTim sets up a list to manage different types of rentals, utilizing the IRental interface to store various rental types, including trucks, sailboats, and cars.
List<IRental> rentals = new List<IRental>
{
new Truck { CurrentRenter = "Truck Renter" },
new Sailboat { CurrentRenter = "Sailboat Renter" },
new Car { CurrentRenter = "Car Renter" }
};Dim rentals As New List(Of IRental) From {
New Truck With {.CurrentRenter = "Truck Renter"},
New Sailboat With {.CurrentRenter = "Sailboat Renter"},
New Car With {.CurrentRenter = "Car Renter"}
}This design allows for looping through the rentals and accessing common properties like CurrentRenter, PricePerDay, and RentalId.
foreach (var rental in rentals)
{
Console.WriteLine($"Renter: {rental.CurrentRenter}, Price Per Day: {rental.PricePerDay}");
}For Each rental In rentals
Console.WriteLine($"Renter: {rental.CurrentRenter}, Price Per Day: {rental.PricePerDay}")
NextTo access specific properties and methods of different rental types, Tim demonstrates how to use the is keyword to check and cast objects to their respective types.
foreach (var rental in rentals)
{
if (rental is Truck truck)
{
Console.WriteLine($"Truck Style: {truck.Style}, Passengers: {truck.NumberOfPassengers}");
}
else if (rental is Sailboat sailboat)
{
// Access sailboat-specific properties
}
else if (rental is Car car)
{
// Access car-specific properties
}
}For Each rental In rentals
If TypeOf rental Is Truck Then
Dim truck As Truck = CType(rental, Truck)
Console.WriteLine($"Truck Style: {truck.Style}, Passengers: {truck.NumberOfPassengers}")
ElseIf TypeOf rental Is Sailboat Then
Dim sailboat As Sailboat = CType(rental, Sailboat)
' Access sailboat-specific properties
ElseIf TypeOf rental Is Car Then
Dim car As Car = CType(rental, Car)
' Access car-specific properties
End If
NextTim emphasizes that using interfaces provides flexibility for future changes. For instance, adding new types of rentals, like tanks or TVs, would not disrupt the existing structure.
Tim advises against overusing inheritance for code sharing, as it can lead to a convoluted and inflexible codebase. Instead, he recommends leveraging interfaces and composition to achieve the desired outcomes without stretching the "is-a" relationship beyond its logical bounds.
Tim Corey's explanation of inheritance and interfaces in OOP offers a clear pathway to creating maintainable and flexible code. By showcasing common pitfalls and providing a refined design with interfaces, he ensures developers can make informed decisions about structuring their applications effectively.
For a deeper dive into these concepts and to see them in action, watch Tim's complete video. His channel is a goldmine of programming tutorials. Don't miss out!
Do you create content for developers working with .NET, C#, Java, Python, or Node.js? Turn your expertise into extra income!
Join our newsletter, you’ll get exclusive access on article updates. We value your privacy