2013-03-13 138 views
3

我有一个类(补丁),我想排序,所以我实现了IComparer。C#排序/比较项目

但是,它需要根据用户想要怎样进行排序,例如: - KEY1,KEY2,KEY3 - KEY1,KEY3,KEY2

对于每个按键比较我已经写了的IComparer类,然而,我想知道如何实现它的连接。即排序时,我只能传递一个IComparer实例。

或者我应该为每种完全排序的IComparer类,即IComparerKey1Key2Key3,IComparerKey1Key3Key2等?

+0

要通过KEY1进行排序,然后按键2然后用东西等? – levi 2013-03-13 10:17:40

回答

6

你可以做一个通用的比较器,需要一个代表来选择关键:

class ByKeyComparer<T, TKey> : IComparer<T> 
{ 
    private readonly Func<T, TKey> _keySelector; 
    private readonly IComparer<TKey> _keyComparer; 

    public ByKeyComparer(Func<T, TKey> keySelector, IComparer<TKey> keyComparer = null) 
    { 
     if (keySelector == null) throw new ArgumentNullException("keySelector"); 
     _keySelector = keySelector; 
     _keyComparer = keyComparer ?? Comparer<TKey>.Default; 
    } 

    public int Compare(T x, T y) 
    { 
     return _keyComparer.Compare(_keySelector(x), _keySelector(y)); 
    } 
} 

有了一个辅助类,以利用类型推断(所以你不需要指定键的类型):

static class ByKeyComparer<T> 
{ 
    public static IComparer<T> Create<TKey>(Func<T, TKey> keySelector, IComparer<TKey> keyComparer = null) 
    { 
     return new ByKeyComparer<T, TKey>(keySelector, keyComparer); 
    } 
} 

你可以像这样使用它:

var patchVersionComparer = ByKeyComparer<Patch>.Create(p => p.Version); 
patches.Sort(patchVersionComparer); 

如果你需要几个键进行比较相结合,您可以创建使用其他comparers一个比较器:

class CompositeComparer<T> : IComparer<T> 
{ 
    private readonly IEnumerable<IComparer<T>> _comparers; 

    public CompositeComparer(IEnumerable<IComparer<T>> comparers) 
    { 
     if (comparers == null) throw new ArgumentNullException("comparers"); 
     _comparers = comparers; 
    } 

    public CompositeComparer(params IComparer<T>[] comparers) 
     : this((IEnumerable<IComparer<T>>)comparers) 
    { 
    } 

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

用法示例:

var comparer = new CompositeComparer<Patch>(
         ByKeyComparer<Patch>.Create(p => p.Key1), 
         ByKeyComparer<Patch>.Create(p => p.Key2), 
         ByKeyComparer<Patch>.Create(p => p.Key3)); 
patches.Sort(comparer); 

编辑:这里有一个更流畅的API :

static class ByKeyComparer<T> 
{ 
    public static IComparer<T> CompareBy<TKey>(Func<T, TKey> keySelector, IComparer<TKey> keyComparer = null) 
    { 
     return new ByKeyComparer<T, TKey>(keySelector, keyComparer); 
    } 
} 

static class ComparerExtensions 
{ 
    public static IComparer<T> ThenBy<T, TKey>(this IComparer<T> comparer, Func<T, TKey> keySelector, IComparer<TKey> keyComparer = null) 
    { 
     var newComparer = ByKeyComparer<T>.CompareBy(keySelector, keyComparer); 

     var composite = comparer as CompositeComparer<T>; 
     if (composite != null) 
      return new CompositeComparer<T>(composite.Comparers.Concat(new[] { newComparer })); 
     return new CompositeComparer<T>(comparer, newComparer); 
    } 
} 

Ex充足:

var comparer = ByKeyComparer<Patch>.CompareBy(p => p.Key1) 
            .ThenBy(p => p.Key2) 
            .ThenBy(p => p.Key3); 
patches.Sort(comparer); 

(很明显,你可能需要添加*Descending版本的CompareByThenBy方法来允许按降序排序)

5

如果你可以使用LINQ,那么很容易对这样的类进行排序。 考虑你有一个ListPatchList<Patch>,你想按key2,key1和key4对它进行排序。你要做的是:

List<Patch> patches = new List<Patch>(); 
patches = GetPatches().ToList().OrderBy(p=>p.Key2).ThenBy(p=>p.Key1).ThenBy(p=>p.Key4).ToList(); 

就是这样。我们爱linq。 :)

如果函数返回列表本身,则不需要第一个ToList

+1

美味的LINQ。 – FLClover 2013-03-13 10:22:16

+2

不幸的是,如果您想对列表进行就地排序,那么这将不起作用......并且,第一个“ToList”是不必要的。 – 2013-03-13 10:22:26

+1

在您的文本中,您已经编写了key4,但是在您的代码中已经写入了key3。 – mgttlinger 2013-03-13 10:23:04