I have a class that implements IEnumerable<T>
:
public class MyList<T> : IEnumerable<T>
{
IQueryable Queryable;
public MyList(IQueryable ts)
{
Queryable = ts;
}
public IEnumerator<T> GetEnumerator()
{
foreach (var item in Queryable)
{
yield return (T)item;
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
When I select some property from IQueryable
and do ToList()
, it works:
IQueryable propertiesFromClasses = classesIQuerable.Select(a => a.Property);
MyList<MyClass> classes = new MyList<MyClass>(propertiesFromClasses);
var toListResult = classes.ToList();
Now, I want to select dynamic types:
IQueryable propertiesFromClasses = classesIQuerable.Select(a => new { SelectedProperty = a.Property });
MyList<MyClass> classes = new MyList<MyClass>(propertiesFromClasses);
var toListResult = classes.ToList();
It throws an exception at yield return (T)item;
of GetEnumerator()
method:
System.InvalidCastException: 'Unable to cast object of type '<>f__AnonymousType0`1[System.String]' to type 'MyClass'
The problem is obviously in the constructor of MyClass<T>
.
First of all, if you want to implement a class MyClass<Order>
that represents an enumerable sequence of Order
objects, why do you allow a constructor that accepts IQueryable<Student>
?
Wouldn't it be better to only accept a sequence of Orders
, or at least a sequence of items that can be converted to Orders
?
This way your compiler will complain, instead of later during execution of the code.
public class MyList<T> : IEnumerable<T>
{
IQueryable<T> queryable;
public MyList(IQueryable<T> ts)
{
this.queryable = ts;
}
public IEnumerator<T> GetEnumerator()
{
return this.Queryable;
}
...
}
If you'd done this, your compiler would already have complained, instead of a run-time exception.
The object that you created in your 2nd piece of code is an object that implements IQueryable<someAnonymousClass>
. Your debugger will tell you that this object does not implement IQueryable<MyClass>
Objects that implement IQueryable<someAnonymousClass>
also implements IQueryable
. However, elements that can be enumerated from this IQueryable
, are of type someAnonymousClass
those objects can't be cast to MyClass
without a proper conversion method.
Your first piece of code does not lead to this problem, because the object that you created implements IQueryable<MyClass>
, and thus the enumerator will yield MyClass
objects, which can be converted to MyClass
objects without problems.
But again: if you had made a proper constructor, you would have noticed the problem at compile time, instead of at run-time.
It could be that you really wanted to design a class that could hold a sequence of Students
in and try to get extract all Orders
from it, or a more realistic problem: put a sequence of Animals
and enumerate all Birds
. Such a class could extract all Birds from any sequence:
public class FilteredEnumerator<T> : IEnumerable<T>
{
public FilteredEnumerator(IQueryable queryable)
{
this.queryable = queryable;
}
private readonly IQueryable queryable;
private IEnumerator<T> GetEnumerator()
{
return this.queryable.OfType<T>();
}
}
Although this would work. I'm not sure if it would be a very meaningful class. There is already a LINQ function that does what you want:
var birds = myDbContext.Animals.OfType();