首页 > 解决方案 > 如何在 GetEnumerator 中返回动态实体

问题描述

我的课程是从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();
        }
    }

当我从中选择一些属性时IQuerableToList()它可以工作:

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

现在,我想选择动态类型:

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

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

System.InvalidCastException:'无法将'<>f__AnonymousType0`1[System.String]'类型的对象转换为'MyClass'类型。'

标签: c#linqdynamicienumerableienumerator

解决方案


问题显然出在MyClass<T>.

做一个合适的构造函数

首先,如果你想实现一个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;
    }
    ...
}

如果你这样做了,你的编译器就会抱怨,而不是运行时异常。

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

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

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

但是再说一遍:如果您创建了一个适当的构造函数,您会在编译时而不是在运行时注意到问题。

替代解决方案

可能是您真的想设计一个可以保存Studentsin 序列并尝试从中提取 all的类Orders,或者是一个更现实的问题:放置一个序列Animals并枚举 all Birds。这样的类可以从任何序列中提取所有 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>();
    }
}

虽然这会奏效。我不确定这是否会是一堂非常有意义的课。已经有一个 LINQ 函数可以满足您的需求:

var bird = myDbContext.Animals.OfType();


推荐阅读