2

I wrote this:

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;
        }
    }
}

Then went to test it with this:

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);
}

The first use of Inspect() works fine. The second one, commented out, won't compile. The return of OrderBy is IOrderedEnumerable. I'd have thought IOrderedEnumerable is-a IEnumerable but, rolling with the punches, I tried:

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;
    }
}

But this won't compile either. I get told I cannot have an iterator block because System.Linq.IOrderedEnumberable is not an iterator interface type.

What am I missing? I cannot see why people wouldn't want to iterate over an ordered collection the same way they do with the raw collection.

(Using Mono 2.10.8.1, which is effectively C# 4.0, and MonoDevelop 2.8.6.3)

UPDATE:

As joshgo kindly pointed out, I can take an input parameter of IOrderedEnumerable, it does indeed act as-a IEnumerable. But to iterate I must return IEnumerable, and my original error was caused by ThenBy, which insists on being given IOrderedEnumerable. Very reasonable too. But is there a way to satisfy ThenBy here?

UPDATE2:

After playing with the code in both answers (both of which were very helpful), I finally understood why I can't use yield with an IOrderedEnumerable return: there is no point, because the values have to be fully available in order to do the sort. So instead of a loop with yield in it, I may as well use a loop to print out all items, then just return source once at the end.

Darren Cook
  • 27,837
  • 13
  • 117
  • 217

2 Answers2

2

I believe an explanation of the error can be found here: Some help understanding "yield"

Quoting Lasse V. Karlsen:

A method using yield return must be declared as returning one of the following two interfaces: IEnumerable or IEnumerator

The issues seems to be with the yield operator and the return type of your second function, IOrderedEnumerable.

If you change the return type from IOrderedEnumerable to IEnumerable, then the 2nd Inspect() call will no longer be an error. However, the ThenBy() call will now throw an error. If you temporarily comment it out, it'll compile but you do lose access to the ThenBy() method.

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;
    }
}
Community
  • 1
  • 1
joshgo
  • 1,164
  • 1
  • 9
  • 14
  • 1
    That doesn't seem to answer the question at all :/ – Earlz Oct 17 '12 at 03:38
  • @Earlz Not at all! (Ah, I see you wrote your comment before joshgo did a major edit.) – Darren Cook Oct 17 '12 at 04:00
  • Aha! You're right, the problem was the call to ThenBy(). In fact `IEnumerable Inspect (this IEnumerable source)` causes no problems with `OrderBy()`. But an override cannot change by just return type; I wonder if there is a way to satisfy ThenBy? – Darren Cook Oct 17 '12 at 04:09
  • You can revert your Inspect function back to returning a IOrderedEnumerable type. Then remove the yield operator and for-loop, and just return an IOrderedEnumerable type instead. It should work, and you should have access to the ThenBy(). Let me know if you need a code snippet. – joshgo Oct 17 '12 at 04:19
2

If you want to apply your extension method after operation, which returns IOrdereEnumerable and continue ordering, then you need to create second overloaded extension:

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);    
}

What is interesting here:

  • You need to return IOrderedEnumerable in order to apply ThenBy or ThenByDescending
  • IOrderedEnumerable is not created via yield return. In your case it could be achieved by creating it from source
  • You should create dummy selector, which does not break ordering of items
  • Output will not contain ordered items, because selector is executed in same order as input sequence.

If you want to see ordered items, you need to execute your OrderedEnumerable. This will force executing of all operators, which present before 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;    
}
Sergey Berezovskiy
  • 232,247
  • 41
  • 429
  • 459
  • Thanks LazyB. Very interesting. I think your second example can be simplified to: `Console.WriteLine("In Ordered Inspect"); foreach(T item in source){Console.WriteLine("Inspect(O):"+item);} return source;` (?) – Darren Cook Oct 21 '12 at 02:57
  • @DarrenCook yes, you are right. If we do not sort anything, we can return source – Sergey Berezovskiy Oct 21 '12 at 03:39