将延迟的IEnumerable <t>拆分为两个序列而不进行重新评估?</t>

时间:2011-06-14 16:37:58

标签: c# .net linq iterator ienumerable

我有一个方法需要处理传入的命令序列,并根据结果的某些属性将结果拆分到不同的桶中。例如:

class Pets
{
    public IEnumerable<Cat> Cats { get; set; }
    public IEnumerable<Dog> Dogs { get; set; }
}

Pets GetPets(IEnumerable<PetRequest> requests) { ... }

底层模型完全能够同时处理整个PetRequest元素序列,而且PetRequest主要是ID等通用信息,因此尝试拆分它是没有意义的。请求输入。但是提供者实际上并没有回馈CatDog个实例,只是一个通用的数据结构:

class PetProvider
{
    IEnumerable<PetData> GetPets(IEnumerable<PetRequest> requests)
    {
        return HandleAllRequests(requests);
    }
}

我已将响应类型命名为PetData而不是Pet,以清楚地表明 <{1}}或Cat的超类 - 换句话说,转换为DogCat是映射过程。要记住的另一件事是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 }; } 结果被枚举两次,但它有两个主要问题:

  1. 它在代码上看起来像一个巨大的疙瘩;它突然出现了我们在LINQ前2.0版框架中必须使用的强大的命令式样式。

  2. 它最终是一个彻头彻尾的毫无意义的练习,因为PetData方法只是将所有这些结果缓存在内存中,这意味着我真的没有比我懒得更好的了。首先做了GroupBy并附上了几个谓词。

  3. 重述问题:

    是否可以将一个延迟的ToList()实例拆分为两个IEnumerable<T>个实例,没有执行任何急切的评估,将结果缓存到内存中,或者必须第二次重新评估原始IEnumerable<?>

    基本上,这与IEnumerable<T>操作相反。 .NET框架中还没有一个这样的事实强烈表明这可能是不可能的,但我认为无论如何都不会有任何问题。

    P.S。请不要告诉我创建一个Concat超类,只返回Pet。我使用IEnumerable<Pet>Cat作为有趣的示例,但实际上结果类型更像DogItem - 它们都来自相同的通用数据,但是没有任何共同之处。

2 个答案:

答案 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 - 这只会评估一次:)