Some of C#'s interfaces are simple and well designed, but the OOP model used is kind of weak for composition of them. For example, one would often like an IIndexable interface to cover both arrays and lists, or an numeric interface for which we can perform common operations over any number type. Without the ability to extend existing implementations with new interfaces, we're stuck using various patterns and wrappers to get the abstraction we need.
I was a C# developer for about 6 years before I got tired of the same old problems and decided it's not for me. I still use it occasionally when required, but not if other options are available.
I’ve thought about this.
To be useful, an IIndexable would need get and set methods for the index, and a Length/Count property. You would need to explicitly implement it on something like the array class since you wouldn’t want to lose the semantic difference between Length (O(1)) and Count (O(n)).
But, the existence of Func<T, TResult> and Action<T1, T2> would largely obsolete the utility of such and interface.
Let xs be an “indexable” object of T (e.g. T[] or List<T>). Then any method that expects to only access an object could just accept lambda of type int -> T. So, you’d just pass it
n => xs[n].
Similarly, methods that expect to mutate the object could take a lambda of type (int, T) -> (). So would just pass something like (n, x) => xs[n] = x.
This is nicer because it makes explicit what the method will do to the indexable. I guess the problem is that it's not discoverable.
I think you are absolutely right. While the reflection is available (and the documentation, in general, is not at all bad), discoverability is still weak.
I was once told that a lot of the original libs were effectively ported from C++ libs by C++ devs in a hurry. There are nuget packages with decent data structure / collections missing from the core BCL.
Interfaces like IEnumerable<T>, IComparable<T> and IEquatable<T> I find to be extremely useful and if you're following interface segregation principle and not chucking round the actual concrete implementations. I concede though that the BCL would be richer if one didn't have to think about this stuff too much.
I guess there's enough experienced C# devs around to know how to avoid those pitfalls. As the BCL is shrunk I expect it will be easier to substitute any structures your not happy with, with alternatives.