I prefer to use IEnumerable<object>
, for LINQ extension methods are defined on it, not IEnumerable
, so that I can use, for example, range.Skip(2)
. However, I also prefer to use IEnumerable
, for T[]
is implicitly convertible to IEnumerable
whether T
is a reference type or value type. For the latter case, no boxing is involved, which is good. As a result, I can do IEnumerable range = new[] { 1, 2, 3 }
. It seems impossible to combine the best of both worlds. Anyway, I chose to settle down to IEnumerable
and do some kind of cast when I need to apply LINQ methods.
From this SO thread, I come to know that range.Cast<object>()
is able to do the job. But it incurs performance overhead which is unnecessary in my opinion. I tried to perform a direct compile-time cast like (IEnumerable<object>)range
. According to my tests, it works for reference element type but not for value type. Any ideas?
FYI, the question stems from this GitHub issue. And the test code I used is as follows:
static void Main(string[] args)
{
// IEnumerable range = new[] { 1, 2, 3 }; // won't work
IEnumerable range = new[] { "a", "b", "c" };
var range2 = (IEnumerable<object>)range;
foreach (var item in range2)
{
Console.WriteLine(item);
}
}
According to my tests, it works for reference element type but not for value type.
Correct. This is because IEnumerable<out T>
is co-variant, and co-variance/contra-variance is not supported for value types.
I come to know that range.Cast() is able to do the job. But it incurs performance overhead which is unnecessary in my opinion.
IMO the performance cost(brought by boxing) is unavoidable if you want a collection of objects with a collection of value-types given. Using the non-generic IEnumerable
won't avoid boxing because IEnumerable.GetEnumerator
provides a IEnumerator
whose .Current
property returns an object
. I'd prefer always use IEnumerable<T>
instead of IEnumerable
. So just use the .Cast
method and forget the boxing.
After decompiling that extension, the source showed this:
public static IEnumerable<TResult> Cast<TResult>(this IEnumerable source)
{
IEnumerable<TResult> enumerable = source as IEnumerable<TResult>;
if (enumerable != null)
return enumerable;
if (source == null)
throw Error.ArgumentNull("source");
return Enumerable.CastIterator<TResult>(source);
}
private static IEnumerable<TResult> CastIterator<TResult>(IEnumerable source)
{
foreach (TResult result in source)
yield return result;
}
This basically does nothing else than IEnumerable<object>
in first place.
You stated:
According to my tests, it works for reference element type but not for value type.
How did you test that?
Despite I really do not like this approach, I know it is possible to provide a toolset similar to LINQ-to-Objects that is callable directly on an IEnumerable
interface, without forcing a cast to IEnumerable<object>
(bad: possible boxing!) and without casting to IEnumerable<TFoo>
(even worse: we'd need to know and write TFoo!).
However, it is:
IEnumerable<T>
it only can throw error or silently cast it to IEnumerable<object>
IEnumerable<int>
and IEnumerable<string>
it simply cannot know what to do; even giving up and casting to IEnumerable<object>
doesn't sound right hereHere's an example for .Net4+:
using System;
using System.Linq;
using System.Collections.Generic;
class Program
{
public static void Main()
{
Console.WriteLine("List<int>");
new List<int> { 1, 2, 3 }
.DoSomething()
.DoSomething();
Console.WriteLine("List<string>");
new List<string> { "a", "b", "c" }
.DoSomething()
.DoSomething();
Console.WriteLine("int[]");
new int[] { 1, 2, 3 }
.DoSomething()
.DoSomething();
Console.WriteLine("string[]");
new string[] { "a", "b", "c" }
.DoSomething()
.DoSomething();
Console.WriteLine("nongeneric collection with ints");
var stack = new System.Collections.Stack();
stack.Push(1);
stack.Push(2);
stack.Push(3);
stack
.DoSomething()
.DoSomething();
Console.WriteLine("nongeneric collection with mixed items");
new System.Collections.ArrayList { 1, "a", null }
.DoSomething()
.DoSomething();
Console.WriteLine("nongeneric collection with .. bits");
new System.Collections.BitArray(0x6D)
.DoSomething()
.DoSomething();
}
}
public static class MyGenericUtils
{
public static System.Collections.IEnumerable DoSomething(this System.Collections.IEnumerable items)
{
// check the REAL type of incoming collection
// if it implements IEnumerable<T>, we're lucky!
// we can unwrap it
// ...usually. How to unwrap it if it implements it multiple times?!
var ietype = items.GetType().FindInterfaces((t, args) =>
t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IEnumerable<>),
null).SingleOrDefault();
if (ietype != null)
{
return
doSomething_X(
doSomething_X((dynamic)items)
);
// .doSomething_X() - and since the compile-time type is 'dynamic' I cannot chain
// .doSomething_X() - it in normal way (despite the fact it would actually compile well)
// `dynamic` doesn't resolve extension methods!
// it would look for doSomething_X inside the returned object
// ..but that's just INTERNAL implementation. For the user
// on the outside it's chainable
}
else
// uh-oh. no what? it can be array, it can be a non-generic collection
// like System.Collections.Hashtable .. but..
// from the type-definition point of view it means it holds any
// OBJECTs inside, even mixed types, and it exposes them as IEnumerable
// which returns them as OBJECTs, so..
return items.Cast<object>()
.doSomething_X()
.doSomething_X();
}
private static IEnumerable<T> doSomething_X<T>(this IEnumerable<T> valitems)
{
// do-whatever,let's just see it being called
Console.WriteLine("I got <{1}>: {0}", valitems.Count(), typeof(T));
return valitems;
}
}
Yes, that's silly. I chained them four (2outsidex2inside) times just to show that the type information is not lost in subsequent calls. The point was to show that the 'entry point' takes a nongeneric IEnumerable
and that <T>
is resolved wherever it can be. You can easily adapt the code to make it a normal LINQ-to-Objects .Count()
method. Similarly, one can write all other operations, too.
This example uses dynamic
to let the platform resolve the most-narrow T for IEnumerable, if possible (which we need to ensure first). Without dynamic
(i.e. .Net2.0) we'd need to invoke the dosomething_X
through reflection, or implement it twice as dosomething_refs<T>():where T:class
+dosomething_vals<T>():where T:struct
and do some magic to call it properly without actually casting (probably reflection, again).
Nevertheless, it seems that you can get something-like-linq working "directly" on things hidden behind nongeneric IEnumerable. All thanks to the fact that the objects hiding behind IEnumerable still have their own full type information (yeah, that assumption may fail with COM or Remoting). However.. I think settling for IEnumerable<T>
is a better option. Let's leave plain old IEnumerable
to special cases where there is really no other option.
..oh.. and I actually didn't investigate if the code above is correct, fast, safe, resource-conserving, lazy-evaluating, etc.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With