2

我写了这个:

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()效果很好。第二个,注释掉,不会编译。的回报OrderByIOrderedEnumerable。我IOrderedEnumerable 原以为是 IEnumerable-a,但是,我用拳头打滚,我试过:

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-a 的作用。但是要迭代我必须返回IEnumerable,而我原来的错误是由ThenBy,坚持给定的IOrderedEnumerable。也很合理。但是这里有办法满足ThenBy吗?

更新2:

在玩过两个答案中的代码之后(这两个答案都非常有帮助),我终于明白为什么我不能使用带有 IOrderedEnumerable 返回的 yield:没有意义,因为这些值必须完全可用才能执行种类。因此,与其使用带有 yield 的循环,不如使用循环打印出所有项目,然后在最后只返回一次 source 。

4

2 回答 2

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;    
}
于 2012-10-17T09:12:45.733 回答
2

我相信可以在这里找到对错误的解释:一些有助于理解“产量”

引用 Lasse V. Karlsen 的话:

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

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

如果将返回类型从 更改IOrderedEnumerableIEnumerable,则第二次 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;
    }
}
于 2012-10-17T03:19:23.407 回答