Had a problem in a complex linq query so I simplified it in LINQPad:
void Main()
{
List<basetype> items = new List<basetype>()
{
new typeA() { baseproperty = "1", extendedproperty = 1 },
new typeB() { baseproperty = "2", extendedproperty = 1.1 },
new typeA() { baseproperty = "3", extendedproperty = 1 },
};
items.Dump();
(from typeA item in items
where item is typeA
select item).Dump();
}
public abstract class basetype
{
public string baseproperty { get; set; }
public string type { get; set; }
}
public class typeA : basetype
{
public int extendedproperty { get; set; }
public typeA() { type = "A"; }
}
public class typeB : basetype
{
public double extendedproperty { get; set; }
public typeB() { type = "B"; }
}
The first Dump works fine and returns:
extendedproperty baseproperty type 1 1 A 1.1 2 B 1 3 A
However the second Dump errors with:
InInvalidCastException: Unable to cast object of type 'typeB' to type 'typeA'.
I can fix this by just removing the “typeA” but I wouldn’t want to do that in the original statement as I would have to cast the type all over the place:
from item in items
Interestingly enough, moving the where also fixes this though you might agree that’s a bit ugly:
from typeA item in items.Where(i => i is typeA)
My question is: why is the original where not filtering out the invalid item before the cast is evaluated?
Reason #1:
The cast to the type happens before the filter because it comes to the left. In C# it is almost always the case that the thing to the left happens before the thing to the right.
Reason #2:
Suppose we did it your way. You have a
List<object>and you sayand you get an error saying that object doesn’t have a Length property – because of course with your way the cast to string happens after the filter, and therefore the filter cannot depend upon the invariant determined by the cast. Presumably you put the cast in there because you want to use the properties of the target type. But you can’t have it both ways; either the left operation runs first or the right operation runs first. They can’t both run first.
Reason #3:
There already is a way to do what you want:
That is equivalent to filtering first and then providing a sequence of just the filtered values of the right type.