6

I want to write an extension method (e.g. .IsEmpty()) for both ICollection and IReadonlyCollection interfaces:

public static bool IsEmpty<T>(this IReadOnlyCollection<T> collection)
{
  return collection == null || collection.Count == 0;
}

public static bool IsEmpty<T>(this ICollection<T> collection)
{
  return collection == null || collection.Count == 0;
}

But when I use it with classes implemeting both interfaces, I obviously get the ‘ambiguous invocation’. I don't want to type myList.IsEmpty<IReadOnlyCollection<myType>>(), I want it to be just myList.IsEmpty().

Is this possible?

user1067514
  • 494
  • 3
  • 15
  • Can't you set one method for `IEnumerable` instead? And btw. implementing both `ICollection` and `IReadOnlyCollection` in the same class seems a bit strange. – MarcinJuraszek Sep 05 '13 at 04:49
  • 3
    Many standard classes implement them both. E.g. `List`, `Dictionary` etc. – user1067514 Sep 05 '13 at 04:55
  • 1
    Either you give them different names or you put them in different static classes in different namespaces. – Mike Zboray Sep 05 '13 at 05:13
  • 1
    @mikez But you shouldn’t have to. Welcome to duck-typing and to MS mistakenly not having `ICollection` implement `IReadOnlyCollection` when the fact that [`List` implements both `IList` and `IReadOnlyList` implies that it should](http://stackoverflow.com/a/15262988/429091). – binki Jan 25 '16 at 16:40
  • @binki It's not a mistake, [it's intentional](https://stackoverflow.com/a/12622784/247702). And interfaces don't implement other interfaces, [they inherit from other interfaces](https://stackoverflow.com/a/807227/247702). – user247702 Oct 26 '17 at 13:04
  • 1
    @Stijn I understand that it is necessary because of backwards compatibility concerns. But note that “reason #2” in the answer you linked is the incorrect misconception that read-only implies immutable. I may say that MS was mistaken because they shipped `ICollection` before they thought of `IReadOnlyCollection`. If .net were to start fresh and did not have a backwards compatibility concern, I’m quite sure they would not hesitate to make `ICollection` be a `IReadOnlyCollection`. I’m also not sure that you can say interfaces inherit either because the members stay with the parent. – binki Oct 26 '17 at 17:01

1 Answers1

3

Given that they both inherit from IEnumerable<T> you could avoid the ambiguity issue by doing an extension on that instead:

public static class IEnumerableExtensions
{
    public static bool IsEmpty<T>(this IEnumerable<T> enumerable)
    {
        return enumerable == null || !enumerable.Any();
    }
}
Timothy Walters
  • 16,866
  • 2
  • 41
  • 49
  • 2
    `enumerable.Count() == 0` enumerates all the items in `enumerable`. It's better to use `!enumerable.Any()`. – user1067514 Sep 05 '13 at 05:45
  • True, I wanted to keep it simple, but that is a valid performance optimization. I'll update my answer. – Timothy Walters Sep 05 '13 at 05:52
  • 1
    This is not the *most* ideal solution if your extension’s implementation needed to know the `Count` of the collection. That property seems to be the one thing that both [`ICollection`](https://msdn.microsoft.com/en-us/library/5s3kzhec%28v=vs.110%29.aspx) and [`IReadOnlyCollection`](https://msdn.microsoft.com/en-us/library/hh881496%28v=vs.110%29.aspx) gain you over `IEnumerable`. However, [the implementation of `Enumerable.Count()` *will* try upcasting to `ICollection` first](https://msdn.microsoft.com/library/bb338038%28v=vs.100%29.aspx), preventing most unnecessary enumeration. – binki Jan 26 '16 at 15:36
  • As binki said, this is not the best approach. For instance, string implements IEnumerable interface but is neither ICollection nor IReadOnlyCollection, and you don't want a string type be accepted by your generic function. – Sackey Jan 25 '20 at 11:05