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.