Подтвердить что ты не робот

Зачем использовать .AsEnumerable(), а не кастинг в IEnumerable <T>?

Один из методов расширения на IEnumerable<T> - .AsEnumerable(). Этот метод преобразует перечислимый объект, на который он был вызван, в экземпляр IEnumerable<T>. Однако, поскольку объект должен реализовывать IEnumerable<T>, чтобы применить к этому методу расширения, преобразование в IEnumerable<T> является простым вопросом приведения к IEnumerable<T>. Мой вопрос в том, почему этот метод существует вообще?

Пример:

List<string> strings = new List<string>() { "test", "test2", "test3" };
IEnumerable<string> stringsEnum1 = strings.AsEnumerable();
IEnumerable<string> stringsEnum2 = (IEnumerable<string>)strings;

В приведенном выше примере stringsEnum1 и stringsEnum2 эквивалентны. Какова точка метода расширения?

Изменить. Как следствие, почему существует метод .AsQueryable(), когда кастинг на IQueryable<T> эквивалентен?

4b9b3361

Ответ 1

Чтение является основной проблемой здесь. Рассмотрим, что

Table.AsEnumerable().Where(somePredicate)

является более читаемым, чем

((IEnumerable<TableObject>)Table).Where(somePredicate).

Или представьте, что хотите выполнить часть запроса на SQL Server, а остальное в памяти:

Table.Where(somePredicate)
     .Select(someProjection)
     .AsEnumerable()
     .SomethingElse()

против

((IEnumerable<SomeProjectionType>)Table.Where(somePredicate)
                                       .Select(someProjection))
                                       .SomethingElse()

Теперь о том, почему такой метод полезен вообще, подумайте о примере Table в LINQ to SQL DataContext. Поскольку Table является IQueryable, он реализует IEnumerable. Когда вы вызываете метод Where для такого Table и перечисляете результаты, выполняется код, который в конечном итоге вызывает выполнение SQL-запроса на SQL Server. Что AsEnumerable does, говорит, нет, я не хочу использовать поставщик LINQ to SQL для выполнения Where, я хочу использовать реализацию LINQ to Objects Where.

Таким образом, перечислив

Table.Where(somePredicate)

приводит к выполнению запроса на SQL Server, тогда как перечисление над

Table.AsEnumerable().Where(somePredicate)

выводит таблицу, представленную Table в память, и выполняет функциональные возможности Where в памяти (а не на SQL Server!)

Это точка AsEnumerable: чтобы вы могли скрыть конкретную реализацию методов IEnumerable и вместо этого использовать стандартную реализацию.

Ответ 2

Я подумал о причине, отличной от удобочитаемости, но связанной с реализацией запроса: использование Linq to Objects для анонимных типов, возвращаемых через другого поставщика Linq. Вы не можете использовать анонимный тип (или коллекцию анонимных типов), но вы можете использовать .AsEnumerable() для выполнения броска для вас.

Пример:

// Get an IQueryable of anonymous types.
var query = from p in db.PeopleTable /* Assume Linq to SQL */
            select new { Name = p.Name, Age = p.Age };

// Execute the query and pull the results into an IEnumerable of anonymous types
var enum = query.AsEnumerable();

// Use Linq to Objects methods to further refine.
var refined = from p in enum
              select new
              {
                  Name = GetPrettyName(p.Name),
                  DOB = CalculateDOB(p.Age, DateTime.Now)
              };

Понятно, что причина в том, что мы хотим использовать что-то вроде Linq to SQL, чтобы вытащить некоторые записи в анонимный тип, а затем выполнить некоторую пользовательскую логику (которая не была бы возможна через Linq to SQL), используя Linq to Objects on клиентская сторона.

Выполнение IEnumerable<_anon> невозможно, поэтому .AsEnumerable() - единственный способ.

Спасибо всем, кто ответил, чтобы помочь мне собрать это вместе. =)

Ответ 3

Это просто самый красивый и самый короткий способ бросить IEnumerable. Если вы посмотрите на него в Reflector, вы увидите, что он ничего не делает, кроме как вернуть объект как IEnumerable.

Из MSDN:

AsEnumerable (из TSource) (IEnumerable (из TSource)) метод не имеет никакого эффекта, кроме изменить тип времени компиляции от типа, который реализует IEnumerable (Of T) для IEnumerable (Of T) сам по себе.

Ответ 4

Как вы говорите, если тип уже реализует IEnumerable<T>, то на самом деле нет никакой функциональной разницы между приведением в интерфейс или вызовом метода AsEnumerable.

Мое предположение, и это только предположение, заключается в том, что вызов AsEnumerable улучшает читаемость и сохраняет свободную подпись других методов расширения LINQ:

var query = ((IEnumerable<YourType>)yourCollection).Select(x => x.YourProperty);

// vs

var query = yourCollection.AsEnumerable().Select(x => x.YourProperty);

Он также допускает типы, которые не реализуют IEnumerable<T> - например, DataTable - чтобы иметь собственную версию AsEnumerable расширение. Это позволяет вам продолжать использовать один и тот же шаблон в запросах для этих типов - даже если это другой метод AsEnumerable, который вы вызываете, - не нужно беспокоиться о том, действительно ли тип реализует IEnumerable<T>.

Ответ 5

Анонимные типы являются основной причиной предоставления таких методов расширения. (вы не можете использовать анонимные типы в параметрах generics) Но вызов метода может использовать вывод типа, позволяющий пропустить указание типа в общих параметрах.

Ответ 6

Как я читаю книгу C# 6.0 in a Nutshell. Ниже приведен пример AsEnumerable в книге.


Цель состоит в том, чтобы сделать последовательность IQueryable<T> равной IEnumerable<T>, заставляя последующие операторы запроса связываться с операторами Enumerable вместо операторов Queryable. Это заставляет остальную часть запроса выполнять локально.

Чтобы проиллюстрировать, предположим, что у нас была таблица MedicalArticles в SQL Server, и мы хотели использовать LINQ to SQL или EF для извлечения всех статей о гриппе, аннотация которого содержала менее 100 слов. Для последнего предиката нам нужно регулярное выражение:

Regex wordCounter = new Regex (@"\b(\w|[-'])+\b");

var query = dataContext.MedicalArticles
            .Where (article => article.Topic == "influenza" &&
            wordCounter.Matches (article.Abstract).Count < 100);

Проблема в том, что SQL Server не поддерживает регулярные выражения, поэтому поставщики LINQ-to-db генерируют исключение, жалуясь на то, что запрос не может быть переведен на SQL. Мы можем решить это путем запроса в два этапа: сначала получить все статьи о гриппе с помощью запроса LINQ to SQL, а затем локально фильтровать тезисы менее 100 слов:

Regex wordCounter = new Regex (@"\b(\w|[-'])+\b");

IEnumerable<MedicalArticle> sqlQuery = dataContext.MedicalArticles
    .Where (article => article.Topic == "influenza");

IEnumerable<MedicalArticle> localQuery = sqlQuery
    .Where (article => wordCounter.Matches (article.Abstract).Count < 100);

С AsEnumerable мы можем сделать то же самое в одном запросе:

var query = dataContext.MedicalArticles
      .Where (article => article.Topic == "influenza")
      .AsEnumerable()
      .Where (article => wordCounter.Matches (article.Abstract).Count < 100);

Альтернативой вызову AsEnumerable является вызов ToArray или ToList. Преимущество AsEnumerable заключается в том, что он не вызывает немедленного выполнения запроса, и не создает никакой структуры хранения.

Ответ 7

Если есть метод для объекта с тем же именем, что и метод расширения Linq, он скрывает метод расширения. Использование AsEnumerable позволяет вам получить расширение.

Это кажется новым в SP1.

Вчера у меня была строка кода, которая извлекала идентификаторы участников из таблицы данных: -

var lMmIds = new List<int>(
    lDmMember.DataTable.Select(R => R.MmId)
);

который работал нормально, пока я не установил SP1. Теперь это не сработает, если оно не прочитает

var lMmIds = new List<int>(
    lDmMember.DataTable.AsEnumerable().Select(R => (int)((dsMtables.tbMMemberRow)R).MmId)
);