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

Какое использование AsEnumerable() для массива?

Я читал блог Эрика Липперта, где объяснил, почему он почти никогда не будет использовать массивы, и следующая часть меня заинтересовала:

Если вы пишете такой API, оберните массив в ReadOnlyCollection и вернете IEnumerable или IList или что-то еще, но не массив. (И, конечно же, не просто бросайте массив в IEnumerable и думайте, что вы сделали! Это все равно передаёт переменные, вызывающий может просто отбрасывать массив! Только передать массив, если он завернут с помощью объекта только для чтения.)

Итак, я немного потрудился с коллекциями:

string[] array = new[] { "cat", "dog", "parrot" };
IEnumerable<string> test1 = array.AsEnumerable();
string[] secondArray = (string[])test1;

//array1[0] is now also "whale"
array[0] = "whale";

//11 interfaces
var iArray = array.GetType().GetInterfaces();
//Still 11 interfaces??
var iTest1 = test1.GetType().GetInterfaces();

Я инициализирую массив, а затем использую метод AsEnumerable() на нем, чтобы преобразовать его в IEnumerable (или так я думал), но когда я вернул его в новый массив и изменил значение в оригинале массив, значения test1 и secondArray были изменены. По-видимому, я просто сделал две новые ссылки на исходный массив, вместо создания нового IEnumerable, немного похожего на ToArray(), возвращает новый массив.

Когда я сравниваю интерфейсы массива и IEnumerable, они оба имеют одинаковые интерфейсы. Почему массив имеет этот метод, если он вообще ничего не делает? Я знаю, что AsEnumerable() использует его с Linq-to-entity для получения перечислимых методов, когда у вас есть IQueryable, но почему этот метод должен быть добавлен в массив? Существует ли практическое применение этого метода?

Изменить: Этот комментарий Тима Шмельтера поднимает действительно хороший момент и не должен оставаться незамеченным:

"Это не так бесполезно. Вы можете изменить фактический тип, не нарушая остальную часть кода. Таким образом, вы можете заменить массив запросом или списком или хешетом базы данных, но AsEnumerable всегда работает, а остальные код тоже. Таким образом, AsEnumerable похож на контракт.

4b9b3361

Ответ 1

AsEnumerable - это просто способ присвоения значения IEnumerable<T>. Он не создает новый объект. Реализация метода следующая:

public static IEnumerable<TSource> AsEnumerable<TSource>(this IEnumerable<TSource> source)
{
    return source;
}

Это не создает новый объект вообще.

Причины полезны:

  • Если объект реализует IEnumerable<T>, но также имеет метод экземпляра с именем Select, Where и т.д., вы не можете использовать метод LINQ. AsEnumerable позволяет использовать его для IEnumerable, чтобы избежать конфликтов с перегрузкой.

  • Если вы хотите поместить объект в IEnumerable<T> (по какой-либо причине), но T является анонимным типом, вы не можете использовать бросок, вам нужен общий метод, который может вывести его общий аргумент для этого.

Ответ 2

Цель AsEnumerable достаточно понятна, как описано в Сервисный ответ, и этот пост, например: Зачем использовать .AsEnumerable() вместо того, чтобы отличать от IEnumerable <T> ?.

Остается вопрос: почему он отображается на IEnumerable<T>? Это просто так, потому что используемый здесь метод расширения использует IEnumerable<T> как тип, на котором он определен. См. Как ToString() на string. Поскольку string - это объект, он выводит метод ToString(), хотя и бесполезен в случае a string. Здесь метод расширения вызывает эту "бесполезную" доступность метода.

Ответ 3

У вас есть несколько хороших ответов здесь; Я думал, что добавлю несколько опорных точек.

Во-первых, FYI, метод "ничего не делать", который всегда возвращает свой аргумент, называется "идентификатором", по той очевидной причине, что вывод идентичен входу. Тождества кажутся бесполезными, но они действительно пригождаются время от времени.

Во-вторых, если вы хотите превратить массив в последовательность, доступную только для чтения, которая не имеет ссылочного идентификатора для массива, вы можете сделать проекцию идентичности:

var sequence = from item in array select item;

или эквивалентно:

var sequence = array.Select(item => item);

Заметим, что проекция здесь является тождеством; Я сказал, что они полезны.

Обычно LINQ будет оптимизировать проецирование идентичности; если вы скажете

from item in array where whatever select item;

то тождественный проектор в конце никогда не генерируется, потому что это просто пустая трата времени. То есть, это означает

array.Where(item => whatever)

не

array.Where(item => whatever).Select(item => item)

Но компилятор не подавляет проекцию тождества в случае, когда select является единственным в запросе, именно так, что вы можете сделать проекцию, которая не может быть возвращена в исходный массив.