2
votes

I wrote code like this:

System.IO.File.ReadAllLines("c://test.txt")
   .Select(val => Console.WriteLine(val)
);

And I'm getting a compiler error:

The type arguments for method 'System.Linq.Enumerable.Select(System.Collections.Generic.IEnumerable, System.Func)' cannot be inferred from the usage. Try specifying the type arguments explicitly.

WriteLine can accept string and object as the parameter, that's probably a source of the error but how do I rewrite it?

4
Please consider rewriting the code to not perform side-effects in LINQ expressions/IEnumerable extension methods (this can have dire consequences when coupled with repeat-evaluations and lazy-sequences). While some people like ForEach (which is a List method, not an IEnumerable method), I recommend (and use religiously) foreach (var elm in enumerable) { ... } to indicate a side-effect process. As a bonus, there is no type inference error. Hint about the error: what is the return type of WriteLine? - user166390
@pst Can you explain more about potential side-effects you mentioned? - Miroslav Zadravec
The ones that you run into when you least expect it ;-) The point is that there is a side-effect occurring. Imagine this: IEnumerable<string> hardToMaintain() { return new [] {"a","b","c"}.Select(e => {Console.WriteLine("" + e); return "dummy"}; }; ... for(var i in hardToMaintain()) { Console.Writeline("bar"); } What is the Output? ForEach being a List method, and not a (lazy) IEnumerable method "fixes" this by forcing the evaluation. However, it doesn't fix the fundamental problem, IMOHO. However, I find use foreach... to indicate that the code has (or may have) side-effects. - user166390
(There is a reason there is no IEnumerable.ForEach or IEnumerable.Select(Action) :-) - user166390

4 Answers

7
votes

The Select method takes a Func<TSource, TResult>, but the return type of Console.WriteLine is void, i.e. no value at all. So in that case TResult would be void, and obviously, the return type of Select can't be IEnumerable<void> (i.e. "a sequence of nothing"...)

The lambda expression passed to Select has to return a value. If you just want to print the result of File.ReadAllLines, use a for or foreach loop.

3
votes

You can fix it by this code:

System.IO.File.ReadAllLines("c://test.txt")
   .Select(val => {Console.WriteLine(val); return val;})

or

System.IO.File.ReadAllLines("c://test.txt")
   .ToList().ForEach(val => Console.WriteLine(val));
3
votes
File.ReadAllLines(@"c://test.txt").ToList().ForEach(Console.WriteLine);
0
votes

This answer includes summary of other answers.

There isn't any problem with parameter type, problem is that Select method must return a value and it shouldn't be used in this case. ForEach would be the correct method, but only List has it, not every IEnumerable. So:

System.IO.File.ReadAllLines("c://test.txt")
.ToList()
.ForEach(val => Console.WriteLine(val));

will do the trick (while incurring some overhead by converting to a list).

There is one more way: static Array.ForEach method. So my code will look like this:

Array.ForEach(
System.IO.File.ReadAllLines("c://test.txt"), val => Console.WriteLine(val)
);