# How .Net breaks Liskov Substitution Principle and Solves it at the same time

Ever traced the .NET collection hierarchy and wondered why `ICollection<T>` has an `IsReadOnly` flag? Let's walk through it.

**It starts with** `IEnumerable<T>` — the most basic contract. Just "give me one item at a time."

```csharp
public interface IEnumerable<T>
{
    IEnumerator<T> GetEnumerator();
}
```

**Then comes** `ICollection<T>` which extends `IEnumerable<T>` and adds mutation:

```csharp
public interface ICollection<T> : IEnumerable<T>
{
    void Add(T item);
    bool Remove(T item);
    void Clear();
    int Count { get; }
    bool IsReadOnly { get; }
}
```

Add and Remove methods to make the collection mutable. Notice how a ReadOnly flag is added here, which will play a key factor later.

**Then** `IList<T>` extends `ICollection<T>` with indexers:

```csharp
public interface IList<T> : ICollection<T>
{
    T this[int index] { get; set; }
    int IndexOf(T item);
    void Insert(int index, T item);
    void RemoveAt(int index);
}
```

Here the List gets the properties or inherits the properties of ICollection which inherits the properties of IEnumerable.

`List<T>` implements `IList<T>` and are fully mutable as expected.

.Net also have `ReadOnlyCollection<T>` which implements `ICollection<T>`. The `ReadOnlyCollection<T>` here is unmuttable but `ReadOnlyCollection<T>` promise to perform `ICollection<T>` tasks.

Here .NET gives you `ReadOnlyCollection<T>` via `AsReadOnly()`

```csharp
ICollection<int> nums = new List<int> { 1, 2, 3 }.AsReadOnly();
nums.Add(4); // Compiles fine... throws error on Runtime
```

**This is a textbook LSP violation.** The principle states: a subtype must be substitutable for its base type *without altering the correctness of the program*. If code trusts `ICollection<T>.Add()` because the interface says it exists, and it explodes at runtime then the substitution has failed.

How .NET "solves" it: It does so at runtime

Instead of splitting the contract cleanly (mutable vs immutable interfaces), .NET bolts on:

*   An `IsReadOnly` **flag**
    
*   A `NotSupportedException`
    

```csharp
if (!nums.IsReadOnly)
    nums.Add(4); // now the caller has to defensively check
```

That defensive `if` is the tell. You shouldn't need to interrogate an object about whether it will honor its own contract, that's exactly what LSP is meant to prevent.

**The takeaway:** Type-checking ≠ behavior-checking. .NET's collection hierarchy compiles cleanly but breaks the substitutability guarantee LSP demands.
