У меня есть метод, который должен обрабатывать входящую последовательность команд и разбивать результаты на разные сегменты в зависимости от некоторых свойств результата.Например:
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
вместо 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<T>
экземпляр на два IEnumerable<?>
экземпляра, без выполнения каких-либо активных оценок, кэшированиеприводит к памяти или необходимости переоценивать оригинал IEnumerable<T>
во второй раз?
По сути, это будет операция, обратная операции Concat
.Тот факт, что в .NET Framework его еще нет, является убедительным свидетельством того, что это может быть даже невозможно, но я подумал, что в любом случае спрашивать не мешало бы.
PS Пожалуйста, донане говорите мне создать Pet
суперкласс и просто вернуть IEnumerable<Pet>
.Я использовал Cat
и Dog
в качестве забавных примеров, но на самом деле типы результатов больше похожи на Item
и Error
- они оба получены из одних и тех же общих данных, но в остальном не имеют ничего общего.