I'm refactoring my code to use IList instead of List. I used List.RemoveAll in a couple of places, and noticed that IList does not have this method at all. Is there any good reason for this?
2 Answers
There is a principle in software engineering called interface segregation. It boils down to the notion that smaller interfaces are better than larger ones. When this idea is taken to the extreme, the ideal interface declares only one member - but let's not bother with that. The point is that interfaces should describe strict requirements, not convenience features.
In our specific case, the IList<T> interface declares the members that a type is required implement in order to be an IList<T>. Obviously, a type isn't required to implement RemoveAll in order to be an IList. It is convenient for a type to do so, but it's not required.
This is a valid use case for extension methods, though. You can define your own RemoveAll extension method for any IList<T> and keep the convenience.
3 Comments
RemoveAll can be implemented in terms of the already exposed members (without knowing its internals), so there's no reason to require the collection to implement it for you.Although a general software design reason was explained by Theodoros Chatzigiannakis you might be interested in a solution when you do need a RemoveAll (maybe you received an IList from a library and want to manipulate that in an easier way). There is an answer here that might interest readers: https://github.com/dotnet/core/issues/2199
Based on the answers from the Dotnet core team, I understand that the main reason is legacy, but as they said, you can easily write an extension method. An example of such an extension method (source: https://www.extensionmethod.net/csharp/icollection-t/removeall)
using System;
using System.Collections.Generic;
using System.Linq;
public static class CollectionExtensions
{
public static void RemoveAll<T>(this ICollection<T> @this, Func<T, bool> predicate)
{
List<T> list = @this as List<T>;
if (list != null)
{
list.RemoveAll(new Predicate<T>(predicate));
}
else
{
List<T> itemsToDelete = @this
.Where(predicate)
.ToList();
foreach (var item in itemsToDelete)
{
@this.Remove(item);
}
}
}
}
IList<T>should have aRemoveAll(Predicate<T>)method.IList<>.