将LINQ(OrderBy / ThenBy)代码转换为CompareTo

时间:2015-02-18 14:36:34

标签: c# linq icomparable

我已经编写了一个通用的排序功能来排序列表和dicitonary。但由于JIT错误,LINQ无法在Unity上运行。我想拥有相同的泛型并将其转换为使用CompraeTo的myList.Sort()。但我无法弄清楚如何实现这个通用。

    public static List<T> MySort<T>(this List<T> source, Type typeOfObject, bool isAscending = false, params string[] param) 
{
    if(param.Length == 0)
        return source;

    if (isAscending)
    {
        var temp = source.OrderBy (a => (typeOfObject.GetProperty (param [0])).GetValue (a, null));

        for (int i=1; i<param.Length; i++)
        {
            var myVar = i;
            temp = temp.ThenBy((a => (typeOfObject.GetProperty(param[myVar])).GetValue (a, null)));
        }
        return temp.ToList();
    }
    else
    {
        var temp = source.OrderByDescending (a => (typeOfObject.GetProperty (param [0])).GetValue (a, null));
        for (int i=1; i<param.Length; i++)
        {
            var myVar = i;
            temp.ThenByDescending((a => (typeOfObject.GetProperty(param[myVar])).GetValue (a, null)));
        }
        return temp.ToList();
    }
}

使用此功能

RealEstateItems.MySort(typeof(mIsoObjectExt), true, "UnlockLevel", "Coins", "Diamonds");

我目前的CompareTo Approac

myList.Sort ((a,b) => {
int result = ((a.Value) as mIsoObjectExt).UnlockLevel.CompareTo(((b.Value) as mIsoObjectExt).UnlockLevel);
//  result == 0 ? result = a.Value.Coins.CompareTo(a.Value.Coins);
if(result == 0)
{
    result = ((a.Value) as mIsoObjectExt).Coins.CompareTo(((b.Value) as mIsoObjectExt).Coins);
}
else
{
    return result;
}
if(result == 0)
{
    return ((a.Value) as mIsoObjectExt).Diamonds.CompareTo(((b.Value) as mIsoObjectExt).Diamonds);
}
return result;
});

但我不满意这一点,每次我必须对相同的属性进行排序时我必须这样做。基本上我想做上面的东西,我告诉函数类型的属性要排序和它排序。我怎么能用Compare / CompareTo做到这一点?

2 个答案:

答案 0 :(得分:1)

因此,我们首先需要一些不同的构建块。首先,你在这里真正做的是对一组值进行排序,如this other question中所示。我们可以从那里提取解决方案,以便根据一组值对项目进行排序:

public class SequenceComparer<T> : IComparer<IEnumerable<T>>
{
    private IComparer<T> comparer;
    public SequenceComparer(IComparer<T> compareer = null)
    {
        this.comparer = comparer ?? Comparer<T>.Default;
    }

    public int Compare(IEnumerable<T> x, IEnumerable<T> y)
    {
        using (var first = x.GetEnumerator())
        using (var second = x.GetEnumerator())
        {
            while (true)
            {
                var firstHasMore = first.MoveNext();
                var secondHasMore = second.MoveNext();
                if (!firstHasMore && !secondHasMore)
                    return 0;
                var lengthComparison = firstHasMore.CompareTo(secondHasMore);
                if (lengthComparison != 0)
                    return lengthComparison;
                var nextComparison = comparer.Compare(first.Current, second.Current);
                if (nextComparison != 0)
                    return nextComparison;
            }
        }
    }
}

我们还想要一种从投影委托创建Comparison<T>委托(List.Sort接受)的方法。这种方法很简单,可写:

public static Comparison<T> CreateComparison<T, TKey>(Func<T, TKey> selector,
    IComparer<TKey> comparer = null)
{
    comparer = comparer ?? Comparer<TKey>.Default;
    return (a, b) => comparer.Compare(selector(a), selector(b));
}

我们能够反转Comparison<T>(处理递减排序)也很有用:

public static Comparison<T> Reverse<T>(this Comparison<T> comparison)
{
    return (a, b) => comparison(b, a);
}

现在将所有部分拉到一起。我们可以创建一个比较,对于投影,将每个项目投影到一系列值中,这些值表示使用反射从项目中获取每个属性名称。如果我们需要降序排序,我们可以反转比较器。

public static void MySort<T>(this List<T> source,
    bool isAscending = false,
    params string[] properties)
{
    var type = typeof(T);
    var comparison = CreateComparison((T item) =>
        properties.Select(prop => type.GetProperty(prop).GetValue(item)),
        new SequenceComparer<object>());
    if (!isAscending)
        comparison = comparison.Reverse();
    source.Sort(comparison);
}

请注意,如果您还可以使用序列比较器来简化LINQ方法:

public static IEnumerable<T> MyOrdering<T>(this IEnumerable<T> source,
    bool isAscending = false,
    params string[] properties)
{
    var type = typeof(T);

    Func<T, IEnumerable<object>> selector = item =>
        properties.Select(prop => type.GetProperty(prop).GetValue(item))
        .ToList();

    if (isAscending)
        return source.OrderBy(selector, new SequenceComparer<object>());
    else
        return source.OrderByDescending(selector, new SequenceComparer<object>());
}

答案 1 :(得分:0)

你可以使用Servy的反射方法。如果你决定反对,你可以使用以下方法,但它仍然需要从调用者提供比较。

public class MultiValueComparer<T> : IComparer<T>
{
   private IEnumerable<Comparison<T>> _comparisons;

   public MultiValueComparer(IEnumerable<Comparison<T>> comparisons)
   {
      _comparisons = comparisons;
   }

   public int Compare(T x, T y)
   {
     foreach (var comparison in _comparisons)
     {
        var result = comparison(x, y);
        if (result != 0)
           return result;
     }
     return 0;
   }
}

一种扩展方法,它采用可变数量的参数

public static void Sort<T>(List<T> source, params Comparison<T>[] comparisons)
{
   if (comparisons.Count() == 0)
      return;

   source.Sort(new MultiValueComparer<T>(comparisons));
}

用法:

升序:

Sort(samples, (x, y) => x.Name.CompareTo(y.Name), (x, y) => x.Test.CompareTo(y.Test));

降序:

Sort(samples, (x, y) => y.Name.CompareTo(x.Name), (x, y) => y.Test.CompareTo(x.Test));