3

我从来没有用IComparer<T>默认构造函数写过有状态的。我在 Reflector 中检查的所有标准库实现也是无状态的。因此,我想假设我可以IComparer<T>像这样自由地缓存:

PriorityQueue<TPriority, TComparer> where TComparer : IComparer<TPriority>, new()
{
    private static TComparer _comparer = new TComparer();

    public PriorityQueue() {...}
    ...
}

代替

PriorityQueue<TPriority>
{
    private IComparer<TPriority> _comparer;

    public PriorityQueue(IComparer<TPriority> comparer) { 
        _comparer = comparer;
        ...
    }

    ...
}

所以这里有一个问题:你有没有写过/看过一个IComparer<T>这会崩溃的?如果是,它有多普遍?

编辑:在这种情况下,我真的不想要第二个版本的开销的原因是数据结构是持久的。它被实现为一棵树,其中节点没有父/根引用。所以它不是每个队列对比较器的一个引用,而是每个节点对比较器的一个引用!我最初的设计只是使用IComparable<T>并建议编写一个包装结构来进行自定义比较。

4

3 回答 3

3

好吧,拥有静态比较器意味着您不能在不同的队列上进行不同的比较;这可能是个问题……有时人们确实需要自定义比较;例如,如果他们不控制类型。我的默认方法是:

PriorityQueue<TPriority>
{
    private IComparer<TPriority> _comparer;

    public PriorityQueue(IComparer<TPriority> comparer) { 
        _comparer = comparer;
        ...
    }

    public PriorityQueue() : this(Comparer<T>.Default) {}
}

重新有状态的比较器;是的,我已经写了几个 - 特别是用于编写 LINQ 风格的投影比较器......例如,类似:

public static class ProjectionComparer<TSource>
{
    public static IComparer<TSource> CompareBy<TValue>(
        Func<TSource, TValue> selector)
    {
        return CompareBy<TValue>(selector, Comparer<TValue>.Default);
    }
    public static IComparer<TSource> CompareBy<TValue>(
        Func<TSource, TValue> selector,
        IComparer<TValue> comparer)
    {
        return new ProjectionComparerItem<TValue>(
            selector, Comparer<TValue>.Default);
    }
    class ProjectionComparerItem<TValue> : IComparer<TSource>
    {
        private readonly IComparer<TValue> comparer;
        private readonly Func<TSource, TValue> selector;
        public ProjectionComparerItem(
            Func<TSource, TValue> selector,
            IComparer<TValue> comparer)
        {
            this.selector = selector;
            this.comparer = comparer;
        }
        public int Compare(TSource x, TSource y)
        {
            // TODO: some null stuff...
            return comparer.Compare(selector(x), selector(y));
        }
    }
}

这使得:

IComparer<Customer> comparer = ProjectionComparer<Customer>
          .CompareBy(cust => cust.Name);

实例“按名称排序”比较。

于 2009-07-21T21:25:12.233 回答
1

是的,我有,但我认为这相当罕见。

在极少数情况下,您希望实现依赖于其他数据的比较。例如,我们有一些空间例程,我们在其中提供一个轴,作为 IComparer 的一部分用于比较。

话虽如此,只需使用一个单独的比较器类就可以很容易地解决这个问题,而且在很多方面这可能是一个更好的设计。IComparer<T>但是,您对确实需要存在的实现进行了限制,因此我将记录您的理由。

我个人的偏好是制作IComparer<T>非静态的,并提供两个构造函数——一个接受外部实例,一个创建默认比较器。每个队列都有一个比较器的额外开销,但这非常小(如果没有状态,则几乎为 0,因为它只是对“空”对象的单个对象引用)。

于 2009-07-21T21:23:50.927 回答
0

另一种可能的方法:

private class PriorityQueueImpl<TPriority, TComparer> where TComparer : IComparer<TPriority> {
    // all methods accept a TComparer
    // generic in TComparer to avoid boxing for struct TComparers and permit inlining for sealed TComparers
}

public struct PriorityQueue<TPriority, TComparer> where TComparer : IComparer<TPriority> {
    private readonly PriorityQueueImpl<TPriority, TComparer> _impl;
    private readonly TComparer _comparer;

    // methods delegate to _impl
}

至少在某些情况下,我可能会同意。

于 2009-07-31T12:56:46.797 回答