The best practices for designing public APIs dictate that returning an interface is a better idea than returning a concrete class, because you can then return a different concrete class if you need to. The consumer of the API is not supposed to rely on the returned instance being of a specific type.
As you point out, ToList
and ToDictionary
return concrete types rather than interfaces. Perhaps the reason for this is that ToList
and ToDictionary
were meant to return you a copy that you can modify should you want to. Since the IList
and IDictionary
interfaces do not guarantee you that they are editable (which was possibly a bad idea), the designers decided to return the concrete type.
Imagine a hypothetical ToIList()
method. If called on an array, it could be implemented to return you a copy of the array, because arrays implement IList<T>
. You might then be surprised to find out that calling Add()
throws.
On the other hand, ILookup
is a read-only interface, so the above doesn't apply. Hence there is no reason not to follow the best practice, like there is with ToList
.