有状态 IComparer是否存在合理的场景?

发布于 2024-07-27 17:02:00 字数 880 浏览 9 评论 0原文

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

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< /code> 这会崩溃吗? 如果是,有多常见?

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

I have never written a stateful IComparer<T> with a default constructor. All standard library implementations which I've checked in Reflector are stateless as well. Therefore, I'd like to assume that I can freely cache IComparer<T> like this:

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

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

instead of

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

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

    ...
}

So here is the question: have you ever written/seen an IComparer<T> for which this would break down? If yes, how common is it?

EDIT: The reason I really don't want the overhead of the second version in this case is that the data structure is persistent. It is implemented as a tree where nodes have no parent/root reference. So it wouldn't be one reference to comparer per queue, but one reference to comparer per node! My original design was just to use IComparable<T> and recommend writing a wrapper struct for custom comparisons.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

·深蓝 2024-08-03 17:02:00

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

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

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

Well, having a static comparer means you can't have different comparisons on different queues; this can be a problem... occasionally people do need a custom comparison; for example, if they don't control the type. My default approach would be:

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

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

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

Re stateful comparers; yes, I've written several - particularly for writing LINQ-style projection comparers... for example, something like:

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

which allows:

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

instance "sort by name" comparison.

巷子口的你 2024-08-03 17:02:00

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

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

话虽这么说,只需使用单独的比较器类就可以很容易地解决这个问题,并且从很多方面来说这可能是更好的设计。 不过,您对确实需要存在的 IComparer 实现施加了限制,因此我会记录您的理由。

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

Yes, I have, but I think it's fairly uncommon.

There are rare instances where you want to implement a comparison that's dependent on other data. For example, we've got some spatial routines where we feed an axis that's used for comparison as part of the IComparer.

That being said, it's pretty easy to work around this by just using a separate comparer class, and that's probably a better design in many ways. You are putting a limitation on the IComparer<T> implementation that does need to exist, though, so I would document your rationale.

My personal preference would be to make the IComparer<T> non-static, and provide two constructors - one that takes an external instance and one that creates a default comparer. You have the extra overhead of a comparer per queue, but that's pretty minimal (nearly 0 if you have no state, since it's only a single object reference to an "empty" object).

天生の放荡 2024-08-03 17:02:00

另一种可能的方法:

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
}

至少在某些情况下我可能会采用它。

Another possible approach:

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
}

I may go with it at least for some cases.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文