实现自定义IComparer<> (附例)

时间:2013-02-05 12:32:33

标签: c# compare icomparer

我刚刚编写了以下代码,它将按原生string.Compare()对字符串进行排序,但允许一组异常(在本例中为customPriority),这些异常优先于默认string.Compare()功能

这看起来有点长篇大论,我想知道是否有一些内置于.NET中的内容允许这样做?

    var unorderered = new[] { "a", "b", "c", "x", "y", "z" };
    var ordered = unorderered.OrderBy(a => a, new CustomStringComparer());
    //expected order y,x,a,b,c,z

class CustomStringComparer : IComparer<string>
{
    int IComparer<string>.Compare(string x, string y)
    {
        if (x == y)
            return 0;
        else
        {
            //----------------------------
            //beginning of custom ordering
            var customPriority = new[] { "y", "x" };
            if (customPriority.Any(a => a == x) && customPriority.Any(a => a == y)) //both in custom ordered array
            {
                if (Array.IndexOf(customPriority, x) < Array.IndexOf(customPriority, y))
                    return -1;                   
                return 1;
            }
            else if (customPriority.Any(a => a == x)) //only one item in custom ordered array (and its x)                    
                return -1;
            else if (customPriority.Any(a => a == y)) //only one item in custom ordered array (and its y)                    
                return 1;
            //---------------------------
            //degrade to default ordering
            else
                return string.Compare(x, y);

        }
    }
}

3 个答案:

答案 0 :(得分:3)

首先,我认为重述这个问题很有用:你想按以下方式排序:

  1. 给定数组中的索引;如果该项不在数组中,则索引为无穷大
  2. 字符串本身
  3. 这意味着您可以使用OrderBy()作为第一个条件,然后使用ThenBy()作为第二个条件来实现排序顺序:

    private static uint NegativeToMaxValue(int i)
    {
        if (i < 0)
            return uint.MaxValue;
        return (uint)i;
    }
    
    …
    
    var ordered = unorderered
        .OrderBy(a => NegativeToMaxValue(Array.IndexOf(new[] { "y", "x" }, a)))
        .ThenBy(a => a);
    

    NegativeToMaxValue()是必要的,因为不在数组中的项应该是最后的,但它们通常是第一个,因为索引是-1。 (采用同样的hackish和不可读的方法是直接将IndexOf()的结果转换为uint。)

    如果您想通过创建IComparer来重复使用此排序,我相信.Net中没有任何内容可以帮助您解决这个问题。但您可以改为使用ComparerExtensions

    IComparer<string> comparer = KeyComparer<string>
        .OrderBy(a => NegativeToMaxValue(Array.IndexOf(new[] { "y", "x" }, a)))
        .ThenBy(a => a);
    

答案 1 :(得分:1)

没有内置的比较方法可以做你想做的事情,但我猜测那不是“啰嗦”的#34;&#34;你正在谈论的部分。

令人讨厌的是,你必须创建一个自定义比较器类,只是为了传递一个简单的比较函数。

嗯,有一种方法可以减轻这种情况。您可以编写几个帮助类,只允许您通过传递方法的名称来使用OrderBy()。如果你编写这些类,它们将适用于所有的OrderBy()语句。

这是一些示例代码。辅助类称为EnumerableExt和ComparisonDelegator。它们协同工作以允许您将方法传递给OrderBy()。

下面的代码显然比你的代码长得多,但请记住,EnumerableExt和ComparisonDelegator类将在一个单独的通用程序集中,所以你不应该计算这些代码。

using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;

namespace Demo
{
    public static class Program
    {
        private static void Main(string[] args)
        {
            var unorderered = new[] { "a", "b", "c", "x", "y", "z" };

            var ordered = unorderered.OrderBy(compare); // Just need to specify the compare method!
        }

        // Each custom compare method must be written specially, as before:

        private static int compare(string x, string y)
        {
            if (x == y)
                return 0;
            else
            {
                //----------------------------
                //beginning of custom ordering
                var customPriority = new[] { "y", "x" };
                if (customPriority.Any(a => a == x) && customPriority.Any(a => a == y)) //both in custom ordered array
                {
                    if (Array.IndexOf(customPriority, x) < Array.IndexOf(customPriority, y))
                        return -1;
                    return 1;
                }
                else if (customPriority.Any(a => a == x)) //only one item in custom ordered array (and its x)                    
                    return -1;
                else if (customPriority.Any(a => a == y)) //only one item in custom ordered array (and its y)                    
                    return 1;
                //---------------------------
                //degrade to default ordering
                else
                    return string.Compare(x, y);

            }
        }
    }

    // The following classes only need to be written once:

    public static class EnumerableExt
    {
        /// <summary>
        /// Convenience method on IEnumerable{T} to allow passing of a
        /// Comparison{T} delegate to the OrderBy method.
        /// </summary>

        public static IEnumerable<T> OrderBy<T>(this IEnumerable<T> list, Comparison<T> comparison)
        {
            Contract.Requires(list != null, "list can't be null.");
            Contract.Requires(comparison != null, "comparer can't be null.");

            return list.OrderBy(t => t, new ComparisonDelegator<T>(comparison));
        }
    }

    /// <summary>
    /// Provides a mechanism for easily converting a Comparison&lt;&gt; delegate (or lambda) to an IComparer&lt;&gt;.
    /// This can be used for List.BinarySearch(), for example.
    /// </summary>
    /// <typeparam name="T">The type of items to be compared.</typeparam>

    public sealed class ComparisonDelegator<T>: IComparer<T>, IComparer
    {
        /// <summary>Create from a Comparison&lt;&gt; delegate.</summary>
        /// <param name="comparison">A Comparison&lt;&gt; delegate.</param>

        public ComparisonDelegator(Comparison<T> comparison)
        {
            Contract.Requires(comparison != null);

            this._comparison = comparison;
        }

        /// <summary>Implements the IComparer.Compare() method.</summary>

        public int Compare(T x, T y)
        {
            return _comparison(x, y);
        }

        /// <summary>Implements the IComparer.Compare() method.</summary>

        public int Compare(object x, object y)
        {
            return _comparison((T)x, (T)y);
        }

        /// <summary>Used to store the Comparison delegate.</summary>

        private readonly Comparison<T> _comparison;
    }
}

然后您也可以按如下方式内联比较方法(但我不建议对于这种复杂的比较方法;这只是为了说明的目的):

    private static void Main(string[] args)
    {
        var unorderered = new[] { "a", "b", "c", "x", "y", "z" };

        var ordered = unorderered.OrderBy((x, y) =>
        {
            if (x == y)
                return 0;
            else
            {
                var customPriority = new[] { "y", "x" };
                if (customPriority.Any(a => a == x) && customPriority.Any(a => a == y)) //both in custom ordered array
                {
                    if (Array.IndexOf(customPriority, x) < Array.IndexOf(customPriority, y))
                        return -1;
                    return 1;
                }
                else if (customPriority.Any(a => a == x)) //only one item in custom ordered array (and its x)                    
                    return -1;
                else if (customPriority.Any(a => a == y)) //only one item in custom ordered array (and its y)                    
                    return 1;
                else
                    return string.Compare(x, y);
            }
        });
    }

答案 2 :(得分:0)

我99.99%肯定,.Net Framework中默认不存在任何此类内容。


您的排序非常自定义,并且不是一般的排序方式,因此默认情况下.NET Framework中不存在这样的事情。