C# 排序/比较项目

3
我有一个类(Patch),我想让它排序,因此实现了IComparer接口。
然而,它需要根据用户的要求进行排序,例如: - key1,key2,key3 - key1,key3,key2
对于每个关键字比较,我都编写了一个IComparer类,但是我想知道如何实现它们的连接。也就是说,在排序时,我只能传递一个IComparer实例。
或者,我应该为每种完全排序创建一个IComparer类,即IComparerKey1Key2Key3,IComparerKey1Key3Key2等?

你想按key1排序,然后按key2排序,然后按某些其他条件排序吗? - levi
3个回答

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);

如果您需要组合多个比较键,可以创建一个使用其他比较器的比较器:

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);
    }
}

例子:

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

(显然,您可能希望添加*Descending版本的CompareByThenBy方法,以允许按降序排序)


5

如果你会使用LINQ,那么对于这样的类进行排序就相当容易了。假设你有一个包含Patch对象的List<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


2
很遗憾,如果您想就地对列表进行排序,那么这种方法是行不通的...另外,第一个“ToList”是不必要的。 - Thomas Levesque
1
在你的文本中,你写了 key4 但是在你的代码中你却写了 key3。 - mgttlinger
我希望能够在另一个解决方案中使用LINQ方法和比较器。比较相当复杂。 - Michel Keijzers

1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接