如何在 GetEnumerator<T> 中 return 动态实体

How to return dynamic entity in GetEnumerator<T>

我从 IEnumerable<T> 实现了 class :

    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();
        }
    }

当我 select 来自 IQuerable 的一些 属性 并执行 ToList() 时,它有效:

        IQueryable propertiesFromClasses = classesIQuerable.Select(a => a.Property);
        MyList<MyClass> classes = new MyList<MyClass>(propertiesFromClasses);
        var toListResult = classes.ToList();

现在,我想要 select 动态类型:

        IQueryable propertiesFromClasses = classesIQuerable.Select(a => new { SelectedProperty = a.Property });
        MyList<MyClass> classes = new MyList<MyClass>(propertiesFromClasses);
        var toListResult = classes.ToList();

它在 GetEnumerator() 方法的 yield return (T)item; 中抛出异常:

System.InvalidCastException: 'Unable to cast object of type '<>f__AnonymousType0`1[System.String]' 键入 'MyClass'.'

问题明显出在MyClass<T>的构造函数中。

制作一个合适的构造器

首先,如果你想实现一个 class MyClass<Order> 来表示 Order 对象的可枚举序列,为什么你允许接受 IQueryable<Student>

只接受 Orders 的序列,或者至少接受可以转换为 Orders 的项目序列,不是更好吗?

这样您的编译器就会报错,而不是稍后在代码执行期间报错。

public class MyList<T> : IEnumerable<T>
{
    IQueryable<T> queryable;
    public MyList(IQueryable<T> ts)
    {
        this.queryable = ts;
    }
    public IEnumerator<T> GetEnumerator()
    {
        return this.Queryable;
    }
    ...
}

如果你这样做了,你的编译器会已经抱怨,而不是运行-time exception。

您在第二段代码中创建的对象是实现 IQueryable<someAnonymousClass> 的对象。你的调试器会告诉你这个对象没有实现 IQueryable<MyClass>

实现 IQueryable<someAnonymousClass> 的对象也实现了 IQueryable。但是,可以从此 IQueryable 枚举的元素属于 someAnonymousClass 类型,如果没有适当的转换方法,这些对象无法转换为 MyClass

你的第一段代码不会导致这个问题,因为你创建的对象实现了IQueryable<MyClass>,因此枚举器将产生MyClass个对象,可以转换为MyClass 没有问题的对象。

但是再说一次:如果你做了一个合适的构造函数,你会在编译时注意到这个问题,而不是在 运行 时。

备选方案

可能是你真的想设计一个 class 可以容纳 Students 的序列并尝试从中提取所有 Orders,或者更现实的问题:放置一个 Animals 的序列并枚举所有 Birds。这样的 class 可以从任何序列中提取所有 Birds:

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>();
    }
}

虽然这行得通。我不确定这是否是一个非常有意义的 class。已经有一个 LINQ 函数可以执行您想要的操作:

var 鸟 = myDbContext.Animals.OfType();