tags:

views:

124

answers:

3

Currently if we get direction of ordering as an external dependency we have to use if to apply this direction:

public static IEnumerable<FileInfo> getlist(string directory, string searchPattern, string order)
{
    var files = new DirectoryInfo(directory).EnumerateFiles(searchPattern);

    if (order == "A")
        return files.OrderBy(f => f.CreationTime);

    return files.OrderByDescending(f => f.CreationTime);
}

Why is there no overload of OrderBy that takes order direction as a parameter? In Reflector I see that it's more or less implemented internally but not exposed for some weird reason.

I would much rather prefer writing something like this:

public static IEnumerable<FileInfo> getlist(string directory, string searchPattern, string order)
{
    return new DirectoryInfo(directory)
        .EnumerateFiles(searchPattern)
        .OrderBy(f => f.CreationTime, order == "A" ? SortOrder.Ascending : SortOrder.Descending);
}

Update:

I can write this myself, just was hoping that it's already in the framework:

public static IOrderedEnumerable<TSource> OrderBy<TSource, TKey>(
    this IEnumerable<TSource> source,
    Func<TSource, TKey> keySelector,
    ListSortDirection order)
{
    switch (order)
    {
        case ListSortDirection.Ascending: return source.OrderBy(keySelector);
        case ListSortDirection.Descending: return source.OrderByDescending(keySelector);
    }

    throw new ArgumentOutOfRangeException("order");
}
+5  A: 

Since a SortOrder enumeration can technically take on more than 2 values (think (SortOrder) 35) it wouldn't capture the duality exactly. Having 2 methods ensures there is no ambiguity or need for range-checking (which is missing from your example btw).

That said, here is the method you want:

public static IOrderedEnumerable<TSource> OrderBy<TSource, TKey>(
    this IEnumerable<TSource> source,
    Func<TSource, TKey> keySelector,
    SortOrder order)
{
    if(order < SortOrder.Ascending || order > SortOrder.Descending)
    {
        throw new ArgumentOutOfRangeException("order");
    }

    return order == SortOrder.Ascending
        ? source.OrderBy(keySelector)
        : source.OrderByDescending(keySelector);
}
Bryan Watts
+1: Exactly what I was going to say.
Robert Harvey
Thanks for the code, that's not the point. Btw, code will be much prettier with switch.
Konstantin Spirin
To capture duality you can use bool but this will decrease readability.
Konstantin Spirin
@Konstantin Spirin: A `switch` statement would throw an `ArgumentOutOfRangeException` in the middle of the method's logic (assuming you meant throw the exception in the `default` case). Coming from the perspective of Code Contracts, preconditions should be declared before the method's logic, hence my choice of structure. Were I to follow your advice, I would throw a non-argument exception type.
Bryan Watts
@Konstatin Spirin: I'm not sure what the point of your question is then. It seems you have asked something only the LINQ designers themselves can answer. Anything from anyone else is just speculation.
Bryan Watts
@Bryan Watts: I was hoping I just missed some part of LINQ and such functionality was there already. Thanks for your comment about Code Contracts - it's a perspective I haven't thought about.
Konstantin Spirin
@Konstantin Spirin: gotcha, I thought your question was more philosophical because it started with "why". It's nice that LINQ is so easy to bend to your will :-)
Bryan Watts
+1  A: 

I don't know why. Though, in the while, you can do it yourself.

public static class IEnumerableSortExtension
{
    public static IOrderedEnumerable<TSource> OrderBy<TSource, TKey>(
            this IEnumerable<TSource> source,
            Func<TSource, TKey> keySelector,
            SortOrder order)
    {
        if (order == SortOrder.Ascending)
            return this.OrderBy(keySelector);
        else if (order == SortOrder.Descending)
            return this.OrderByDescending(keySelector);
        throw new InvalidOperationException(); // do something better than this
    }
}
zneak
+4  A: 

The OrderBy method already has the flexibility that you require, and more, because it can take an optional IComparer<T> argument:

return new DirectoryInfo(directory)
    .EnumerateFiles(searchPattern)
    .OrderBy(f => f.CreationTime, order == "A"
                                      ? Comparer<DateTime>.Default
                                      : new DescendingComparer<DateTime>);

// ...

public DescendingComparer<T> : Comparer<T>
{
    public override int Compare(T x, T y)
    {
        return Comparer<T>.Default.Compare(y, x);
    }
}
LukeH
That's a good one!
Konstantin Spirin
@Konstantin: And, of course, if you found that you needed extra comparison strategies other than plain asc or desc then you could simply create an appropriate comparer.
LukeH