NGI
NGI

Reputation: 902

Generic Return Type of Linq Query and String.Join bad use

EDIT Summary after solution had been given

*Linq query results are almost always presented with "var" implicit type or with typed IENumerable< T> like IEnumerable< int>. However simple more generic IEnumerable is sufficient

*String.Join() does not have specialization for IEnumerable ( but has naturally one for IEnumerable< T> ) and fall back to String.Join Method (String, Object[] ...). It then assumes that the linq query result res is an array of Object of size 1. I do not know why there is no overload for IEnumerable!

/EDIT

I had imagine that it would be possible to write generic linq query return types. A first idea was to write a code like this:

class exLinq
{
    Func<IEnumerable>[] fun_list = new Func<IEnumerable>[]
    { () => {   int[]   data    =       { 1, 2, 5, 8, 11 };
                var     result  =       from d in data
                                        where 0 == d % 2
                                        select d;
                return result;
            }
      //() => Other queries that could be on string, double... collections
     };



    static void Main(string args[])
    {         
        foreach (var fun in fun_list)
        {
            var f = fun;
            var res = f();
            Console.WriteLine(String.Join(", ",res));
        }
    }
}

However I got the result

System.Linq.Enumerable+WhereArrayIterator`1[System.Int32]

My linq query return type would be more adequat with IEnumerable < int > ( It works when I do this ) but I was wondering if I could mix in the same array/list queries that would have handle strings,double or whatsoever...

Up to now I got a correct code output with the modification

if ( res is IEnumerable<int>)
    Console.WriteLine(String.Join(", ",res.Cast<int>()));

2, 8

More accustomed with C++, I had believed that I would be able to handle any "groups of object" with the "higher" class.

Any idea of how to correctly achieve this?

Upvotes: 1

Views: 572

Answers (1)

Lee
Lee

Reputation: 144206

In this case you can do it if you convert each enumerable to an object[] before calling string.Join:

foreach (var fun in fun_list)
{
    var f = fun;
    var res = f();
    Console.WriteLine(String.Join(", ",res.Cast<object>().ToArray()));
}

The reason you're seeing this behaviour is that

string.Join(", ", res);

calls this overload of string.Join. The params modifier on the last array parameter allows it to be called with any number of parameters e.g.

string.Join(", ", 1, 2, 3);

Each of these parameters get added to an object[] which is passed to the method. The Join method calls ToString on each element in the array to format it in the output string.

This means that when you call

string.Join(", ", res);

the ToString method of the generated IEnumerable is called, which is where "System.Linq.Enumerable+WhereArrayIterator`1[System.Int32]" comes from. You can overcome this behaviour by creating the object[] yourself and passing it to string.Join.

You can also use

string.Join(", ", res.Cast<object>());

this resolves to a different overload of string.Join which calls ToString on each element of the input sequence.

Upvotes: 1

Related Questions