Aar*_*ght 9 .net c# linq ienumerable iterator
我有一个方法需要处理传入的命令序列,并根据结果的某些属性将结果拆分到不同的桶中.例如:
class Pets
{
public IEnumerable<Cat> Cats { get; set; }
public IEnumerable<Dog> Dogs { get; set; }
}
Pets GetPets(IEnumerable<PetRequest> requests) { ... }
Run Code Online (Sandbox Code Playgroud)
底层模型完全能够PetRequest
一次处理整个元素序列,而且PetRequest
大部分都是ID等通用信息,因此尝试在输入处拆分请求是没有意义的.但是提供者实际上并没有回馈Cat
和Dog
实例,只是一个通用的数据结构:
class PetProvider
{
IEnumerable<PetData> GetPets(IEnumerable<PetRequest> requests)
{
return HandleAllRequests(requests);
}
}
Run Code Online (Sandbox Code Playgroud)
我已经命名了响应类型,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 };
}
Run Code Online (Sandbox Code Playgroud)
这在技术上是有效的,因为它不会导致PetData
结果被枚举两次,但它有两个主要问题:
它在代码上看起来像一个巨大的疙瘩; 它有点像我们在LINQ前2.0版框架中必须使用的糟糕的命令式样式.
它最终是一个彻头彻尾的毫无意义的练习,因为这个GroupBy
方法只是将所有这些结果缓存在内存中,这意味着我真的没有比我只是懒惰并且ToList()
在第一时间完成一个并附加一些谓词的情况更好..
所以重申一下这个问题:
是否可以将单个延迟IEnumerable<T>
实例拆分为两个IEnumerable<?>
实例,而不执行任何急切评估,将结果缓存到内存中,或者必须再次重新评估原始实例IEnumerable<T>
?
基本上,这将是一个Concat
操作的反向..NET框架中还没有一个这样的事实强烈表明这甚至不可能,但我认为无论如何都不会有任何问题.
PS请不要告诉我创建一个Pet
超类并返回一个IEnumerable<Pet>
.我曾经Cat
和Dog
有趣的例子,但在现实的结果类型更像是Item
和Error
-它们都来自同一个通用数据衍生而来,但否则没有什么共同之处.
Jon*_*eet 12
从根本上说,没有.试想一下,如果它是可能的.然后考虑如果我这样做会发生什么:
foreach (Cat cat in pets.Cats)
{
...
}
foreach (Dog dog in pets.Dogs)
{
...
}
Run Code Online (Sandbox Code Playgroud)
这需要首先处理所有的猫,然后是所有的狗...所以如果第一个元素是一个原始序列会发生什么Dog
?它要么必须缓存它,要么跳过它 - 它不能返回它,因为我们仍然要求它Cats
.
你可以实现一些只需要缓存的东西,但这可能是整个一个序列,因为典型的用法是完全评估一个序列或另一个序列.
如果可能的话,你真的只想在你拿走它们时处理宠物(无论是猫还是狗).难道是可行的提供Action<Cat>
和Action<Pet>
和执行正确的处理程序为每个项目?