为什么不能使用IOrderedEnumerable的迭代器块

时间:2012-10-17 02:41:36

标签: c# .net linq mono iterator

我写了这个:

using System;using System.Linq;
static class MyExtensions
{
    public static IEnumerable<T> Inspect<T> (this IEnumerable<T> source)
    {
        Console.WriteLine ("In Inspect");
        //return source;    //Works, but does nothing
        foreach(T item in source){
            Console.WriteLine(item);
            yield return item;
        }
    }
}

然后去测试它:

var collection = Enumerable.Range(-5, 11)
    .Select(x => new { Original = x, Square = x * x })
    .Inspect()
    .OrderBy(x => x.Square)
    //.Inspect()
    .ThenBy(x => x.Original)
    ;
foreach (var element in collection)
{
Console.WriteLine(element);
}

首次使用Inspect()可以正常使用。第二个,注释掉,不会编译。 OrderBy的返回值为IOrderedEnumerable。我原以为IOrderedEnumerable 是-a IEnumerable但是,我试着用拳头试试:

public static IOrderedEnumerable<T> Inspect<T> (this IOrderedEnumerable<T> source)
{
    Console.WriteLine ("In Inspect (ordered)");
    foreach(T item in source){
        Console.WriteLine(item);
        yield return item;
    }
}

但这也不会编译。我被告知我不能有一个迭代器块,因为System.Linq.IOrderedEnumberable不是一个迭代器接口类型。

我错过了什么?我不明白为什么人们不想像对原始集合那样迭代有序集合。

(使用Mono 2.10.8.1,实际上是C#4.0,以及MonoDevelop 2.8.6.3)

更新

正如joshgo所指出的,我可以采用IOrderedEnumerable的输入参数,确实将作为 IEnumerable。但要迭代我必须返回IEnumerable,我的原始错误是由ThenBy引起的,IOrderedEnumerable坚持给予ThenBy。也很合理。但有没有办法在这里满足{{1}}?

UPDATE2:

在使用两个答案中的代码(两者都非常有用)之后,我终于理解为什么我不能使用带有IOrderedEnumerable返回的yield:没有意义,因为值必须完全可用做那种。因此,我可以使用循环打印出所有项目,然后在结尾处返回源代码,而不是带有yield的循环。

2 个答案:

答案 0 :(得分:2)

我相信错误的解释可以在这里找到:Some help understanding "yield"

引用Lasse V. Karlsen:

  

使用yield return的方法必须声明为返回其中一个   以下两个接口:IEnumerable或IEnumerator

问题似乎与yield运算符和第二个函数IOrderedEnumerable的返回类型有关。

如果您将返回类型从IOrderedEnumerable更改为IEnumerable,那么第二次Inspect()调用将不再是错误。但是,ThenBy()调用现在会抛出错误。如果您暂时将其注释掉,它将会编译,但您无法访问ThenBy()方法。

var collection = Enumerable.Range(-5, 11)
    .Select(x => new { Original = x, Square = x * x })
    .Inspect()
    .OrderBy(x => x.Square)
    .Inspect()
    //.ThenBy(x => x.Original)
    ;
foreach (var element in collection)
{
    Console.WriteLine(element);
}

...

public static IEnumerable<T> Inspect<T> (this IOrderedEnumerable<T> source)
{
    Console.WriteLine ("In Inspect (ordered)");
    foreach(T item in source){
        Console.WriteLine(item);
        yield return item;
    }
}

答案 1 :(得分:2)

如果要在操作后应用扩展方法,返回IOrdereEnumerable并继续排序,则需要创建第二个重载扩展:

public static IOrderedEnumerable<T> Inspect<T>(this IOrderedEnumerable<T> source)
{
    Console.WriteLine("In Ordered Inspect");
    // inspected items will be unordered
    Func<T, int> selector = item => { 
              Console.WriteLine(item); 
              return 0; };

    return source.CreateOrderedEnumerable(selector, null, false);    
}

这里有趣的是:

  • 您需要返回IOrderedEnumerable才能应用ThenByThenByDescending
  • IOrderedEnumerable不是通过yield return创建的。在您的情况下,可以通过从源
  • 创建它来实现
  • 您应该创建虚拟选择器,它不会破坏项目的排序
  • 输出不包含有序项,因为选择器的执行顺序与输入顺序相同。

如果您想查看订购商品,则需要执行OrderedEnumerable。这将强制执行在Inspect之前出现的所有运算符:

public static IOrderedEnumerable<T> Inspect<T>(this IOrderedEnumerable<T> source)
{
    Console.WriteLine("In Ordered Inspect");            
    var enumerable = source.CreateOrderedEnumerable(x => 0, null, false);    
    // each time you apply Inspect all query until this operator will be executed
    foreach(var item in enumerable)
        Console.WriteLine(item);
    return enumerable;    
}