我有一个方法需要处理传入的命令序列,并根据结果的某些属性将结果拆分到不同的桶中。例如:
class Pets
{
public IEnumerable<Cat> Cats { get; set; }
public IEnumerable<Dog> Dogs { get; set; }
}
Pets GetPets(IEnumerable<PetRequest> requests) { ... }
底层模型完全能够同时处理整个PetRequest
元素序列,而且PetRequest
主要是ID等通用信息,因此尝试拆分它是没有意义的。请求输入。但是提供者实际上并没有回馈Cat
和Dog
个实例,只是一个通用的数据结构:
class PetProvider
{
IEnumerable<PetData> GetPets(IEnumerable<PetRequest> requests)
{
return HandleAllRequests(requests);
}
}
我已将响应类型命名为PetData
而不是Pet
,以清楚地表明 <{1}}或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 };
}
结果被枚举两次,但它有两个主要问题:
它在代码上看起来像一个巨大的疙瘩;它突然出现了我们在LINQ前2.0版框架中必须使用的强大的命令式样式。
它最终是一个彻头彻尾的毫无意义的练习,因为PetData
方法只是将所有这些结果缓存在内存中,这意味着我真的没有比我懒得更好的了。首先做了GroupBy
并附上了几个谓词。
重述问题:
是否可以将一个延迟的ToList()
实例拆分为两个IEnumerable<T>
个实例,没有执行任何急切的评估,将结果缓存到内存中,或者必须第二次重新评估原始IEnumerable<?>
基本上,这与IEnumerable<T>
操作相反。 .NET框架中还没有一个这样的事实强烈表明这可能是不可能的,但我认为无论如何都不会有任何问题。
P.S。请不要告诉我创建一个Concat
超类,只返回Pet
。我使用IEnumerable<Pet>
和Cat
作为有趣的示例,但实际上结果类型更像Dog
和Item
- 它们都来自相同的通用数据,但是没有任何共同之处。
答案 0 :(得分:12)
从根本上说,没有。想象一下,如果 可能。然后考虑如果我这样做会发生什么:
foreach (Cat cat in pets.Cats)
{
...
}
foreach (Dog dog in pets.Dogs)
{
...
}
首先需要处理所有的猫,然后是所有的狗...所以如果第一个元素是Dog
,原始序列会发生什么?它要么必须缓存它或跳过它 - 它不能返回它,因为我们仍然要求Cats
。
你可以实现只能按需要缓存的内容,但可能是整个一个序列,因为典型的用法是完全评估一个序列序列或其他。
如果可能的话,你真的只想在你拿走它们时处理宠物(无论是猫还是狗)。是否可以提供Action<Cat>
和Action<Pet>
并为每个项目执行正确的处理程序?
答案 1 :(得分:2)
Jon said(我确信我是第100万人)。
我可能只是去上学并做:
List<Cat> cats = new List<Cat>();
List<Dog> dog = new List<Dog>();
foreach(var pet in data)
{
if (g.Sound == "Bark")
dogs.Add(ConvertDog(pet));
else if (pet.Sound == "Meow")
cats.Add(ConvertCat(pet));
}
但是我意识到这并不是你想要做的 - 但是你确实说过 re -evaluation - 这只会评估一次:)