Вопрос кастинга C #: от IEnumerable к пользовательскому типу - PullRequest
2 голосов
/ 25 мая 2010

У меня есть собственный класс с именем Rows, который реализует IEnumerable<Row>. Я часто использую запросы LINQ на Rows экземплярах:

Rows rows = new Rows { row1, row2, row3 };
IEnumerable<Row> particularRows = rows.Where<Row>(row => condition);

Что бы я хотел, чтобы я мог делать следующее:

Rows rows = new Rows { row1, row2, row3 };
Rows particularRows = (Rows)rows.Where<Row>(row => condition);

Однако я получаю «System.InvalidCastException: Невозможно привести объект типа« WhereEnumerableIterator1 [NS.Row] »к типу« NS.Rows ». У меня есть Rows конструктор, принимающий IEnumerable<Row>, поэтому я мог бы сделать:

Rows rows = new Rows { row1, row2, row3 };
Rows particularRows = new Rows(rows.Where<Row>(row => condition));

Однако это кажется громоздким, и я бы хотел иметь возможность разыграть IEnumerable<Row>, чтобы он стал Rows, поскольку Rows реализует IEnumerable<Row>. Есть идеи?

Ответы [ 7 ]

4 голосов
/ 25 мая 2010

метод расширения для спасения.

public static Rows ToRows(this IEnumerable<Row> source)
{
  return new Rows(source);
}

Rows specificRows = rows.Where (...). ToRows ();


«Кастинг» не подходит для этой проблемы. Каждый Rows является IEnumerable<Row>, но не каждый IEnumerable<Row> является Rows

2 голосов
/ 25 мая 2010

Вы можете приводить объект к определенному классу только в том случае, если этот объект на самом деле является экземпляром этого класса.

В вашем случае IEnumerable<Row>, который вы получаете из вызова LINQ, не является Rows экземпляра, и среда выполнения .Net не может волшебным образом преобразовать его в Rows экземпляр.

Вместо этого вам нужно создать функцию, которая создает новый Rows экземпляр из IEnumerable<Row>.Эта функция, вероятно, должна быть либо перегрузкой, либо методом расширения.

Обратите внимание, кстати, что стандартные соглашения о присвоении имен .Net указывают, что ваш класс Rows действительно должен называться RowCollection.

1 голос
/ 25 мая 2010

Вы должны помнить, что, хотя вы можете создать цепочку наследования (Row -> IEnumerable), нет надежного способа свернуть цепочку наследования.

Обратите внимание на следующее:

List<Row> rows = new List<Row>();

// Fill the list
IEnumerable<Row> rowsAsEnumerable = (IEnumerable<Row>)rows;

Rows realRows = (Rows)rowsAsEnumerable;

Так как rowAsEnumerable никогда не был объектом Rows, как система будет обрабатывать приведение?

Ответ таков: это невозможно, потому что, хотя объект Rows всегда является IEnumerable, объект IEnumerable не всегда является объектом Rows.

Обновление

Поскольку мне не нравится быть разрушителем снов, я еще раз повторю то, что сказали другие.

Вы можете использовать простой метод Extension, чтобы сделать вещи немного чище из вашего примера:

public static Rows ToRows(this IEnumerable<Row> rowsAsEnum)
{
    return new Rows(rowsAsEnum);
}

Теперь вы можете изменить свой звонок на:

Rows particularRows = rows.Where<Row>(row => condition).ToRows();
1 голос
/ 25 мая 2010

Хотя Rows может реализовывать IEnumerable<Row>, это не означает, что любой IEnumerable<Row> является Rows, поэтому компилятор не может просто позволить вам сделать это предположение.

0 голосов
/ 25 мая 2010

В основном, любое решение, которое вы придумали здесь, будет включать обтекание этого конструктора, который принимает IEnumerable

Мой первоначальный ответ определил решение для кастинга (но мне не удалось скомпилировать и увидеть ошибку).

Что бы я сделал, это определил метод расширения для IEnumerable:

public static Rows ToRows(this IEnumerable<Row> rows)
{
  return new Rows(rows);
}

Тогда вы можете использовать его довольно чисто:

public void Foo(IEnumerable<Row> rows)
{
   Rows r = rows.Where(r => condition).ToRows();
}
0 голосов
/ 25 мая 2010

Вам необходимо определить явное приведение в вашем классе:

public class Rows : IEnumerable<Row>
{
    public static explicit operator MyType(IEnumerable<Row> x)
    {
        return new Rows(x); // using your constructor
    }
}

Это скажет C # разрешить ваше приведение:

Rows particularRows = (Rows)rows.Where<Row>(row => condition);
0 голосов
/ 25 мая 2010

Это работает?

Rows particularRows = (Rows)(rows.Where<Row>(row => condition).ConvertAll(r=>(Row)r));

Вы также можете добавить обертку метода расширения для вышеперечисленного (что будет самым чистым решением), чтобы вы могли сделать что-то вроде:

Rows particularRows = rows.Where<Row>(row => condition).ToRows();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...