我写了这个:
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的循环。
答案 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
才能应用ThenBy
或ThenByDescending
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;
}