Reputation: 622
I am having a hard time understanding why I am getting the results that I am.
I have two lists of strings:
var list1 = new List<String> {"item 1", "item 2"};
var list2 = new List<String> { "item 3", "item 4" };
Version 1 - Output: "item 2"
var item =
from x in (list1.Concat(list2))
where x.EndsWith("2")
select x;
Console.WriteLine(item.First());
Version 2 - Output: "i"
var item =
from x in (list1.Concat(list2))
where x.EndsWith("2")
select x.First();
Console.WriteLine(item.First());
Version 3 - Output: "System.Linq.Enumerable+WhereEnumerableIterator`1[System.String]"
var item =
from x in (list1.Concat(list2))
where x.EndsWith("2")
select x;
Console.WriteLine(item);
Given that version 2 outputs "i", I would expect version 3 to output "item 2". Why is this behavior occurring?
Upvotes: 4
Views: 5279
Reputation: 776
//This raise a exception if no item found
var item=list1.Concat(list2).First(i => i.EndsWith("2"));
//this return default value (null) if no item found
var item2 = list1.Concat(list2).FirstOrDefault(i => i.EndsWith("2"));
Upvotes: 0
Reputation: 144122
In version 3, select x
is returning a sequence of strings that match your critera; it just happens to be a sequence with one item in it.
Console.WriteLine
internally calls .ToString()
on whatever you pass into it. Since there is no meaningful string representation for IEnumerable<T>
, the default in .NET is to print the string name of the type.
Based on your wording, I think part of your confusion does come from a misunderstanding of why version 2 works the way it does. In version 2, select x.First()
is actually a bit of a quirk/coincidence, because a string is also an IEnumerable<char>
, so you can do LINQ operations on the string. .First()
returns the first element of that char
sequence, for each result that matches your criteria. So you're saying:
"For each element which matches my criteria, select the first character, and then return the sequence of all the first characters for the matches."
So in fact, item
in version 2 is an IEnumerable<char>
with one element in it. Calling Console.WriteLine()
on an IEnumerable<char>
will just print the chars in order. So you get "i".
(note: I see after I answered this, the question was edited to call .First()
both inside the projection and on the result, so the bit about passing IEnumerable<char>
to Console.WriteLine
isn't totally relevant anymore)
Keep in mind LINQ is pretty much about working with sets until you explicitly reduce them down. For example, Select
is simply a projection or transformation. It returns the same number of items that were passed to it, transformed. Where
reduces down the set, but it's still a set.
Upvotes: 5
Reputation: 223267
Your Version 2 is selecting first item/char from the string x.First()
, whereas your first version is selecting first item from the result set-> First string.
Version 1 is like - Select First Item from the Result Set
var item = (from x in (list1.Concat(list2))
where x.EndsWith("2")
select x).First(); //First complete string will be selected
and version 2 is like- Select First Item from a string in result set
var item = from x in (list1.Concat(list2))
where x.EndsWith("2")
select x.First(); //only first char will be selected for string
Your third case is selecting an IEnumerable<string>
, so when you call Console.WriteLine
, it calls the default implementation of ToString
and thus you get
"System.Linq.Enumerable+WhereEnumerableIterator`1[System.String]"
Upvotes: 2
Reputation: 54524
The type of the x
in your Version 2 is String
. The type of item
in Version 1 is IEnumerable.
So your Version 2 return a list of characters which are the first characters of each string. In your Version 1, item.First()
return the first element of the result set which is a string.
Upvotes: 0
Reputation: 61349
Because Where
returns an IEnumerable
.
You have written the equivalent of:
var whoKnows = list1.Concat(list2).Where(x => x.EndsWith("2"));
Console.WriteLine(whoKnows);
The ToString
of a collection just returns the class name.
Upvotes: 0
Reputation: 149538
When using First()
you are materializing the list, causing the iterator to execute. That is eager execution. Your third version uses select
which does not materialize the list abd uses Defeered Execution
which returns an iterator, hence calling ToString()
on it returning the iterators name
Upvotes: 0