Преобразование IEnumerable в IEnumerable

#c# #generics #inheritance #casting #.net-2.0

#c# #обобщения #наследование #Кастинг #.net-2.0

Вопрос:

У меня есть класс, который я использую для перечисления списков из моей базы данных. Все таблицы в базе данных имеют свой собственный класс со своими собственными свойствами, и все они являются производными от DatabaseTableRow.

 public class DatabaseLinkRowsEnumerator<T> : IEnumerator<T>, IEnumerable<T> where T : DatabaseTableRow
  

У меня есть пользовательский класс, который является производным от Page, который является производным от DatabaseTableRow. Затем у меня есть свойство, которое возвращает DatabaseLinkRowsEnumerator, список пользователей.

У меня также есть функция пользовательского интерфейса, которая отображает списки любых страниц в горизонтальном списке с изображением, именем и ссылкой.

 protected string GetVerticalListModuleHtml(IEnumerable<Page> pages)
  

Теперь все, что я хочу сделать, это передать этой функции имеющееся у меня значение DatabaseLinkRowsEnumerator. Пользователь является производным от страницы, а DatabaseLinkRowsEnumerator является IEnumerator. Даже когда я пытаюсь выполнить приведение, я получаю следующую ошибку:

 Unable to cast object of type 'Database.DatabaseLinkRowsEnumerator`1[Database.User]' to type 'System.Collections.Generic.IEnumerable`1[Database.Page]'.
  

Я использую ASP.NET 2.0. Есть ли у кого-нибудь идеи о том, как преобразовать это, не создавая полную копию каждого?

Ответ №1:

При использовании .NET 2.0 это немного неудобно, но не слишком сложно:

 public static IEnumerable<TBase> Cast<TDerived, TBase>
    (IEnumerable<TDerived> source)
    where TDerived : TBase
{
    foreach (TDerived item in source)
    {
        yield return item;
    }
}
  

Назовите это следующим образом:

 IEnumerable<Page> pages = UtilityClass.Cast<User, Page>(users);
  

Это приведет к ленивой оценке. В LINQ все проще — вы используете Cast метод расширения:

 var baseSequence = derivedSequence.Cast<BaseClass>();
  

В .NET 4 IEnumerable<T> является ковариантным в T , поэтому преобразование ссылочного типа из IEnumerable<DerivedClass> в IEnumerable<BaseClass> уже есть 🙂

Комментарии:

1. Это верно, если наследование настроено правильно, но в сообщении OP я не вижу связи между DatabaseTableRow и Page.

2. @Justin, в OP сказано, что страница является производной от DatabaseTableRow

3. Однако здесь я немного запутался.. Куда бы вы поместили этот метод?

4. @Connell: В любом случае — в служебном классе, например. Просто вызовите его из любого места, где вам нужна последовательность.

5. @Connell: Я забыл включить параметры типа в метод, что, вероятно, не помогло. Я обновил метод и привел пример.

Ответ №2:

Вы можете это сделать:

 DatabaseLinkRowsEnumerator<User> users = ...
IEnumerable<Page> = users.Cast<Page>();
  

РЕДАКТИРОВАТЬ: Cast метод расширения недоступен в .NET 2.0. Вместо этого вы можете использовать реализацию Джона Скита как обычный статический метод. Если вы используете VS2008, вы также можете использовать LINQBridge, который позволяет вам использовать Linq для объектов при настройке .NET 2.0

Комментарии:

1. Не является ли Enumerable.Cast<>() методом LINQ, представленным в .NET 3.5?