У меня есть метод, который должен обрабатывать входящую последовательность команд и разбивать результаты на разные сегменты в зависимости от некоторых свойств результата. Например:
class Pets
{
public IEnumerable<Cat> Cats { get; set; }
public IEnumerable<Dog> Dogs { get; set; }
}
Pets GetPets(IEnumerable<PetRequest> requests) { ... }
Базовая модель полностью способна обрабатывать всю последовательность элементов PetRequest
одновременно, а также PetRequest
в основном является общей информацией, такой как идентификатор, поэтому он делает нет смысла пытаться разбивать запросы на входе. Но на самом деле провайдер не возвращает экземпляры Cat
и Dog
, а только общую структуру данных:
class PetProvider
{
IEnumerable<PetData> GetPets(IEnumerable<PetRequest> requests)
{
return HandleAllRequests(requests);
}
}
Вместо этого я назвал тип ответа PetData
of Pet
, чтобы четко указать, что это не суперкласс Cat
или Dog
- другими словами, преобразование в Cat
или Dog
- это процесс отображения. Также следует иметь в виду, что HandleAllRequests
стоит дорого, например запрос к базе данных, поэтому я действительно не хочу его повторять, и я бы предпочел избегать кеширования результатов в памяти с помощью ToArray ()
или тому подобного, потому что может быть тысячи или миллионы результатов (у меня много домашних животных).
Пока что мне удалось собрать воедино этот неуклюжий прием:
Pets GetPets(IEnumerable<PetRequest> requests)
{
var data = petProvider.GetPets(requests);
var dataGroups =
from d in data
group d by d.Sound into g
select new { Sound = g.Key, PetData = g };
IEnumerable<Cat> cats = null;
IEnumerable<Dog> dogs = null;
foreach (var g in dataGroups)
if (g.Sound == "Bark")
dogs = g.PetData.Select(d => ConvertDog(d));
else if (g.Sound == "Meow")
cats = g.PetData.Select(d => ConvertCat(d));
return new Pets { Cats = cats, Dogs = dogs };
}
Технически это работает, в том смысле, что это не так. не приводит к двойному перечислению результатов PetData
, но имеет две основные проблемы:
Это похоже на гигантскую прыщику в коде; это попахивает ужасным императивным стилем, который мы всегда использовали в pre-LINQ framework 2.0.
Это заканчивается совершенно бессмысленным упражнением, потому что метод GroupBy
просто кэширует все эти результаты в памяти, а это значит, что мне действительно не лучше, чем если бы я просто поленился и сделал ToList ()
в первую очередь и добавил несколько предикатов.
Итак, чтобы повторить вопрос :
Можно ли разделить один отложенный экземпляр IEnumerable
на два экземпляра IEnumerable >
, без выполнения каких-либо активных оценок, кэширования приводит к появлению памяти или необходимости повторно оценивать исходный IEnumerable
во второй раз?
По сути, это было бы обратной операцией Concat
. Тот факт, что его еще нет в .NET framework, является убедительным свидетельством того, что это может быть даже невозможно, но я подумал, что в любом случае не повредит спросить.
P.S. Пожалуйста, не говорите мне создать суперкласс Pet
и просто вернуть IEnumerable
. Я использовал Cat
и Dog
в качестве забавных примеров, но на самом деле типы результатов больше похожи на Item
и Error
- они оба являются производными из одних и тех же общих данных, но в остальном не имеют ничего общего.