I have the following:
foreach (ItemOption itemOption in p.Items.Select(e => e.ItemOption).GroupBy(e => e.Id))
{ // do some work on itemoptions }
This compiles.
However at runtime I get an Invalid Cast exception:
Unable to cast object of type ‘Grouping[System.String,MyNameSpace.ItemOption]’ to type ‘MyNameSpace.ItemOption’.
If I change the code to, e.g. a String as the type of the item :
foreach (String itemOption in p.Items.Select(e => e.ItemOption).GroupBy(e => e.Id))
{ // do some work on itemoptions }
Then the compiler tells me the types are incompatible.
Why doesn’t the compiler flag the type incompatibility in the first code block?
I did some further investigation and found that, given the following code:
var foo = p.Items.Select(e => e.ItemOption).GroupBy(e => e.Id));
Type singleElementType = foo.ElementAt(0).GetType();
singleElementType is:
System.Linq.Lookup`2+Grouping[System.String, MyNamespace.ItemOption]
UPDATE
Following on from the answers I have put together a simpler case to demonstrate the issue
Given the objects:
interface IMyObj
{
string Id;
}
class MyObj : IMyObj
{
public string Id;
public MyObj2 cg;
}
class MyObj2
{
}
This will fail at compile time
IEnumerable<MyObj> compileTimeFailList = new List<MyObj>()
foreach (MyObj2 myObj2 in compileTimeFailList.Where(x => x.Id != null))
{
and this will fail at run time
IEnumerable<IMyObj> runtimeFailList = new List<IMyObj>();
foreach (MyObj2 myObj2 in runtimeFailList.Where(x => x.Id != null))
{
The reason being that the objects in runtimeFailList may extend MyObj2 and this cannot be determined by the compiler.
ItemOptionis presumably not a sealed class (unlike System.String) so it’s possible that the result ofp.Items.Select(...).GroupBy(...)would be implementations ofIGrouping<...>which were alsoItemOptionvalues. The compiler can’t know, so it inserts an implicit cast. Asstringdoesn’t implementIGrouping<...>and is sealed, the compiler can spot that that’s definitely a mistake.foreachhas always included a cast where necessary. It’s a bit nasty, because it’s hidden… but without it,foreachwould have been very painful to use pre-generics.Now as for why it’s actually wrong… each item in the result is going to be a grouping rather than an individual item. Let me know if you need more help working with the results.