Encountering a System.InvalidCastException: Unable to cast object of type 'System.Collections.Generic.List`1[Interface]' to type 'System.Collections.Generic.List`1[ConcreteType]' is a classic rite of passage for C# developers. Even if every element inside your collection is guaranteed to be an instance of the derived class, the direct cast fails at runtime. Let's explore why this happens and how to resolve it cleanly using modern C# practices.

Understanding Why Direct Casting Fails (Lack of Variance)

In C#, List<T> is an invariant generic class. Even though Category implements INonJunctionEntity, List<Category> is not a subclass of List<INonJunctionEntity>. Because classes are invariant, C# refuses to treat the list itself as interchangeable, preventing runtime issues like adding an incompatible entity into what should be a category-only list.

Trying to bypass compiler checks via (List<Category>)(object)list simply defers the check to the Common Language Runtime (CLR), resulting in a runtime InvalidCastException.

The Solution: Converting Elements via LINQ

To convert an existing List<Parent> or List<Interface> into a List<Child>, you must transform the elements individually rather than attempting to cast the collection container as a whole.

Approach 1: Using LINQ's Cast<T>()

If you are confident that every single element inside the source collection actually implements or inherits from Category, LINQ’s Cast<T>() method is the cleanest approach:

using System.Linq;

List<INonJunctionEntity> sourceList = X();
List<Category> categories = sourceList.Cast<Category>().ToList();

Approach 2: Using LINQ's OfType<T>() (Safer Alternative)

If the collection might contain instances of other classes implementing INonJunctionEntity, Cast<Category>() will throw an InvalidCastException when it encounters a non-matching item. To avoid runtime exceptions and filter out only matching types, use OfType<T>():

using System.Linq;

List<INonJunctionEntity> sourceList = X();
List<Category> categories = sourceList.OfType<Category>().ToList();

Why Did Cast<T>() Work for You but Fail for Others?

A common point of confusion is why parentList.Cast<ChildClass>().ToList() solves the problem in some cases but throws an exception in others. The distinction lies in the actual runtime types of the underlying objects:

  • Scenario A (Your Case): The items were initially created as new Category() and placed into a List<INonJunctionEntity>. Because the underlying object in memory is a Category, casting each element succeeds.
  • Scenario B (The Failing Case): The items were instantiated purely as base classes (e.g., new ParentClass(), such as instances coming straight from an API or ORM). A base class instance cannot be cast downward to a derived type because it lacks the derived class's properties and behaviors.

Alternative: Use Covariant Interfaces like IEnumerable<T>

If you only need read-only access to the list, consider leveraging covariance. While List<T> is invariant, IEnumerable<out T> and IReadOnlyList<out T> are covariant:

// Covariance allows this without casting:
IEnumerable<Category> categories = GetCategories();
IEnumerable<INonJunctionEntity> entities = categories; // Allowed!

Note that covariance only works from derived-to-base (outward), not base-to-derived (inward), which is why creating a new list with .Cast<T>().ToList() or .OfType<T>().ToList() remains the standard solution for downcasting collections.