Given Array.Cast<T>(), how do I determine T via reflection?

Viewed 68

TL;DR - I would expect these all to work the same, but (per comments) they do not:

var c1 = new[] { FileMode.Append }.Cast<int>();
var c2 = new[] { FileMode.Append }.Select(x => (int)x);
var c3 = new[] { FileMode.Append }.Select(x => x).Cast<int>();

foreach (var x in c1 as IEnumerable)
  Console.WriteLine(x); // Append (I would expect 6 here!)

foreach (var x in c2 as IEnumerable)
  Console.WriteLine(x); // 6

foreach (var x in c3 as IEnumerable)
  Console.WriteLine(x); // 6

This is a contrived example; I obviously wouldn't cast the collections to IEnumerable if I didn't have to, and in that case everything would work as expected. But I'm working on a library with several methods that take an object and return a serialized string representation. If it determines via reflection that the object implements IEnumerable, it will enumerate it and, in almost all cases, return the expected result...except for this strange case with Array.Cast<T>.

There's 2 things I could do here:

  1. Tell uses to materialize IEnumerables first, such as with ToList().
  2. Create an overload for each affected method that takes an IEnumerable<T>.

For different reasons, neither of those is ideal. Is it possible for a method that takes an object to somehow infer T when Array.Cast<T>() is passed?

1 Answers

Is it possible for a method that takes an object to somehow infer T when Array.Cast() is passed?

No, not in the example you gave.

The reason you get the output you do is that the Enumerable.Cast<T>() method has an optimization to allow the original object to be returned when it's compatible with the type you ask for:

public static IEnumerable<TResult> Cast<TResult>(this IEnumerable source) {
    IEnumerable<TResult> typedSource = source as IEnumerable<TResult>;
    if (typedSource != null) return typedSource;
    if (source == null) throw Error.ArgumentNull("source");
    return CastIterator<TResult>(source);
}

So in your first case, nothing actually happens. The Cast<T>() method is just returning the object you passed into the method, and so by the time you get it back, the fact that it ever went through Cast<T>() is completely lost.

Your question doesn't have any other details about how you got into this situation or why it matters in a practical sense. But we can say conclusively that given the code you posted, it would be impossible to achieve the goal you've stated.

Related