13

I would like to work with ordered enumerables, and use interfaces as return types rather than the concrete types. I need to return an ordered set of objects. But, when using an IList<T> implementation I can not return IOrderedEnumerable<T>, as IList<T> does not inherit IOrderedEnumerable<T>.

In the example below I have a view model with a repository of series, implemented as a List<T> of series objects, which are, as they are residing in a List<T>, ordered. I an accessor method, I want to return a filtered set of the series where only series objects of a specific type are returned, while keeping the original order among the filtered elements.

/// <summary>
/// Represents the view model for this module.
/// </summary>
public class ViewModel : AbstractViewModel
{
    /// <summary>
    /// Gets the series repository.
    /// </summary>
    /// <value>The series repository.</value>
    public IList<ISeries> SeriesRepository { get; private set; }

    //...
}

//8<-----------------------------

    /// <summary>
    /// Gets the series of the specified type.
    /// </summary>
    public IOrderedEnumerable<T> Series<T>() where T : ISeries
    {
        return ViewModel.SeriesRepository.OfType<T>(); //compiler ERROR
    }

The compiler tells me:

Error   14  Cannot implicitly convert type 'System.Collections.Generic.IEnumerable<T>' to 'System.Linq.IOrderedEnumerable<T>'. An explicit conversion exists (are you missing a cast?) ...

How can I support such a scenario? And why does List not implement IOrderedEnumerable?

EDIT: To clarify my intentions: I simply want to declare at the interface level, that my Repository has an order, even if it is not explicitly specified by a key. Thus, .ThenBy et.al. should not add a new order, as there is already one - my own one and only one. :-). I see, that like so, I miss the intention of .ThenBy.

Marcel
  • 15,039
  • 20
  • 92
  • 150

2 Answers2

17

How could List<T> implement IOrderedEnumerable<T>? It would have to provide a way of creating a subsequent ordering... what does that even mean?

Consider this:

var names = new List<string> { "Jon", "Holly", "Ash", "Robin", "William" };
var ordered = names.ThenBy(x => x.Length);

what does that even mean? There's no primary sort order (as there would be if I used names.OrderBy(x => x)), so it's impossible to impose a secondary sort order.

I suggest you try creating your own implementation of IOrderedEnumerable<T> based on a List<T> - as you attempt to implement the CreateOrderedEnumerable method, I think you'll see why it's inappropriate. You may find my Edulinq blog post on IOrderedEnumerable<T> useful.

ruffin
  • 16,507
  • 9
  • 88
  • 138
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • .ThenBy should just add no secondary order, it should just return the original sequence. I see that this misses the idea of it. – Marcel Mar 25 '11 at 10:06
  • 2
    @Marcel: Exactly - you just *can't* implement it in a meaningful way. That's why it *doesn't* implement it. – Jon Skeet Mar 25 '11 at 10:26
  • 8
    I would say that a List is ordered by the indices of the items inside it. Hence it is only logical that ThenBy would not change anything because there are no equals as defined by the original ordering. – Matthijs Wessels Aug 28 '12 at 14:46
  • 1
    Damn, it took me a while to get this but this is right. The problem is that List is ordered arbitrarily (you could say its insert order), while `IOrderedEnumerable` has a defined sort order, as if it was just ordered via some sorting function.. Perhaps it should be called `ISortedEnumerable` – Yarek T Jan 04 '22 at 16:11
9

Well, you are wrong: List<T> is NOT ordered by a particular key. The elements inside the list are in the order you put them in. That's the reason, why List<T> doesn't implement IOrderedEnumerable<T>.
Just return the following:

ViewModel.SeriesRepository.OfType<T>().OrderBy(<your order predicate>);
Daniel Hilgarth
  • 171,043
  • 40
  • 335
  • 443
  • 11
    @Daniel: I would say a `List` *is* ordered: as you say, they're "in the order you put them in". That's an ordering, but it's not an ordering where it makes any sense to impose a "secondary" ordering. – Jon Skeet Mar 25 '11 at 08:21
  • 2
    @Jon: My understanding is different. An ordered enumeration is one, that is always ordered by a key, *independent* of the order the elements have been inserted into the list. – Daniel Hilgarth Mar 25 '11 at 08:25
  • 3
    -1: As the OP, I would like to emphasize that the "order I put them in" is exactly the order I do need, even if there is no explicit key. In the example, the repository's order would be intentionally created. – Marcel Mar 25 '11 at 08:32
  • 1
    @Daniel: That's the meaning of IOrderedEnumerable, yes, but it's not the meaning of the word "ordered" in general. Imagine we weren't talking about LINQ - it would be entirely reasonable to say "One difference between `HashSet` and `List` is that the list is ordered, but the set isn't." If you iterate over the list, you *know* the order in which the values will be returned. Just changing your description to "is not ordered by any particular key" would make it clearer. – Jon Skeet Mar 25 '11 at 08:33
  • 1
    @Marcel: So what would you expect the results of calling `ThenBy` to be? That's the bit you still haven't made clear. – Jon Skeet Mar 25 '11 at 08:33
  • 1
    @Marcel: Your downvote teaches me to not try helping you again. After all, I provided you with an explanation why it isn't working and a way around it. @Jon: Updated my answer as suggested. The different understanding might come from the fact that I am not a native speaker. But it looks like even the MS engineers are seeing "order" and "sort" as equivalent. `IOrderedEnumerable` <-> `SortedList` <-> `OrderBy` etc... – Daniel Hilgarth Mar 25 '11 at 08:38
  • 1
    @Daniel: The downvote originally was meant to disagree with "not ordered", but with the update suggested by Jon, your post is clear to me. – Marcel Mar 25 '11 at 09:55
  • 3
    Where does it say that an IOrderedEnumerable has to be ordered by a particular key? – Matthijs Wessels Aug 28 '12 at 14:37