How do I apply a default IComparable<T> in a Linq OrderBy clause
Asked Answered
S

2

7

I have a type which has a default sort order as it implements IComparable<T> and IComparable. I'm not getting the results I expect from LINQ , basically it looks as if the IComparable<T> which the type implements is not being applied.

I thought I would get the result I want with an expression in the form:

var result = MyEnumerable<T>.OrderBy(r => r); 

where T itself implements IComparable<T>. It's not happening.

I can see related questions where specific IComparable<T> classes are specified for the sort, but I can't find one which uses the default IComparable<T> implemented by T itself.

My syntax is clearly incorrect. What is the correct syntax please?

Thanks in advance.

Shive answered 9/8, 2012 at 9:44 Comment(3)
Shouldn't you inherit IEquatable for LINQ?Stickle
Post your IComparable<T> implementation please.Vulpecula
Welcome to SO, Mike. I edited away your name from the question, as per the FAQ. If "Mike" really is your name, you might want to use that in your profile instead of the boring user1587065.Codycoe
C
11

OrderBy uses the default comparer Comparer<T>.Default which in turn will default to use the IComparable<T> implementation for T, or the non-generic IComparable if the former does not exist.

This code works:

public class Program
{
    static void Main(string[] args)
    {
        var list = new List<Stuff>
                       {
                           new Stuff("one"),
                           new Stuff("two"),
                           new Stuff("three"),
                           new Stuff("four")
                       };

        var sorted = list.OrderBy(x => x);

        foreach (var stuff in sorted)
        {
            Console.Out.WriteLine(stuff.Name);
        }
    }
}

public class Stuff : IComparable<Stuff>
{
    public string Name { get; set; }

    public Stuff(string name)
    {
        Name = name;
    }

    public int CompareTo(Stuff other)
    {
        return String.CompareOrdinal(Name, other.Name);
    }
}
Codycoe answered 9/8, 2012 at 10:30 Comment(3)
Thanks for your swift response Christoffer. I'm reasonably happy with my IComparable&ltT> implementation : I've checked it with the List&ltT>.Sort() method. Is there a distinction if the sequence is a List&lt;T>, which implements a Sort() method? My full syntax, which I now realise I should have written in full, sorry! ) is : var result = someQueryable&ltT>.Where(x=>x.Property == value).OrderBy(r=>r); I don't materialize the query result ( for example with ToList() ) before invoking OrderBy(r=>r).Shive
@Shive There's no difference in sorting between IEnumerable<T>.OrderBy(r => r) and List<T>.Sort(). They both use the default comparer Comparer<T>.Default. The difference is of course that .Sort() rearranges the elements of the List<T>, while .OrderBy(...) doesn't.Codycoe
@Shive Do you need any more help with this problem, or is it solved? Don't hesitate to comment if you're not satisfied with the answer. On the other hand, if you're satisfied with the answer, please mark the answer as accepted. Thank you.Codycoe
E
-1
public static class GenericSorter
{
    public static IOrderedEnumerable<T> Sort<T>(IEnumerable<T> toSort, Dictionary<string, SortingOrder> sortOptions)
    {
        IOrderedEnumerable<T> orderedList = null;

        foreach (KeyValuePair<string, SortingOrder> entry in sortOptions)
        {
            if (orderedList != null)
            {
                if (entry.Value == SortingOrder.Ascending)
                {
                    orderedList = orderedList.ApplyOrder<T>(entry.Key, "ThenBy");
                }
                else
                {
                    orderedList = orderedList.ApplyOrder<T>(entry.Key, "ThenByDescending");
                }
            }
            else
            {
                if (entry.Value == SortingOrder.Ascending)
                {
                    orderedList = toSort.ApplyOrder<T>(entry.Key, "OrderBy");
                }
                else
                {
                    orderedList = toSort.ApplyOrder<T>(entry.Key, "OrderByDescending");
                }
            }
        }

        return orderedList;
    }

    private static IOrderedEnumerable<T> ApplyOrder<T>(this IEnumerable<T> source, string property, string methodName)
    {
        ParameterExpression param = Expression.Parameter(typeof(T), "x");
        Expression expr = param;
        foreach (string prop in property.Split('.'))
        {
            expr = Expression.PropertyOrField(expr, prop);
        }
        Type delegateType = typeof(Func<,>).MakeGenericType(typeof(T), expr.Type);
        LambdaExpression lambda = Expression.Lambda(delegateType, expr, param);

        MethodInfo mi = typeof(Enumerable).GetMethods().Single(
                method => method.Name == methodName
                        && method.IsGenericMethodDefinition
                        && method.GetGenericArguments().Length == 2
                        && method.GetParameters().Length == 2)
                .MakeGenericMethod(typeof(T), expr.Type);
        return (IOrderedEnumerable<T>)mi.Invoke(null, new object[] { source, lambda.Compile() });
    }
}
Ethiopic answered 21/11, 2012 at 0:17 Comment(1)
Please describe your code and don't just put a code up with no explanation, even if the code might be useful.Mixedup

© 2022 - 2024 — McMap. All rights reserved.