EDIT:
From the answers given, it's been made rather clear to me how the design I'm asking about below should actually be implemented. With those suggestions in mind (and in response to a comment politely pointing out that my example code does not even compile), I've edited the following code to reflect what the general consensus seems to be. The question that remains may no longer make sense in light of the code, but I'm leaving it as it is for posterity.
Suppose I have three overloads of a function, one taking IEnumerable<T>
, one taking ICollection<T>
, and one taking IList<T>
, something like the following:
public static T GetMiddle<T>(IEnumerable<T> values) {
IList<T> list = values as IList<T>;
if (list != null) return GetMiddle(list);
int count = GetCount<T>(values);
T middle = default(T);
int index = 0;
foreach (T value in values) {
if (index++ >= count / 2) {
middle = value;
break;
}
}
return middle;
}
private static T GetMiddle<T>(IList<T> values) {
int middleIndex = values.Count / 2;
return values[middleIndex];
}
private static int GetCount<T>(IEnumerable<T> values) {
// if values is actually an ICollection<T> (e.g., List<T>),
// we can get the count quite cheaply
ICollection<T> genericCollection = values as ICollection<T>;
if (genericCollection != null) return genericCollection.Count;
// same for ICollection (e.g., Queue<T>, Stack<T>)
ICollection collection = values as ICollection;
if (collection != null) return collection.Count;
// otherwise, we've got to count values ourselves
int count = 0;
foreach (T value in values) count++;
return count;
}
The idea here is that, if I've got an IList<T>
, that makes my job easiest; on the other hand, I can still do the job with an ICollection<T>
or even an IEnumerable<T>
; the implementation for those interfaces just isn't as efficient.
I wasn't sure if this would even work (if the runtime would be able to choose an overload based on the parameter passed), but I've tested it and it seems to.
My question is: is there a problem with this approach that I haven't thought of? Alternately, is this in fact a good approach, but there's a better way of accomplishing it (maybe by attempting to cast the values
argument up to an IList<T>
first and running the more efficient overload if the cast works)? I'm just interested to know others' thoughts.