Как вернуть динамический объект в GetEnumerator <T> - PullRequest
0 голосов
/ 03 мая 2019

У меня реализован класс от 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();
        }
    }

Когда я выбираю какое-либо свойство из IQuerable и выполняю ToList(), оно работает:

        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 ». '

1 Ответ

0 голосов
/ 03 мая 2019

Проблема, очевидно, заключается в конструкторе 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.Однако элементы, которые можно перечислить из этого IQueryable, относятся к типу someAnonymousClass, и эти объекты нельзя преобразовать в MyClass без надлежащего метода преобразования.

Ваш первый фрагмент кода не приводитк этой проблеме, потому что созданный вами объект реализует IQueryable<MyClass>, и, следовательно, перечислитель выдаст MyClass объектов, которые можно без проблем преобразовать в MyClass объектов.

Но опять же: если у вас былоСделав правильный конструктор, вы заметили бы проблему во время компиляции, а не во время выполнения.

Альтернативное решение

Возможно, вы действительно хотели разработать класс, который мог бы содержатьпоследовательность Students и попытайтесь извлечь из нее все Orders, или более реалистичная задача: поместите последовательность Animals и перечислите все 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 birds = myDbContext.Animals.OfType ();

...