实现自定义IComparer<>;(举个例子)
本文关键字:自定义 IComparer lt gt 实现 | 更新日期: 2023-09-27 17:57:59
我刚刚编写了以下代码,它将按字符串的本机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);
}
}
}
首先,我认为重述这个问题很有用:您希望按排序
- 给定数组中的索引;如果项不在数组中,则索引为无穷大
- 字符串本身
这意味着您可以通过使用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。(一种很难理解的方法是直接将IndexOf()
的结果转换为uint
。)
如果您想通过创建IComparer
来重用这种排序,我相信.Net中没有任何东西可以帮助您。但是您可以使用CompareExtension:
IComparer<string> comparer = KeyComparer<string>
.OrderBy(a => NegativeToMaxValue(Array.IndexOf(new[] { "y", "x" }, a)))
.ThenBy(a => a);
没有内置的比较方法来做你想做的事情,但我猜这不是你所说的"长篇大论"的部分。
令人讨厌的是,您必须创建一个自定义比较器类来传递一个简单的比较函数。
好吧,有一种方法可以缓解这种情况。您可以编写几个助手类,只需传递一个方法的名称就可以使用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<> delegate (or lambda) to an IComparer<>.
/// 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<> delegate.</summary>
/// <param name="comparison">A Comparison<> 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;
}
}
然后,您也可以按照如下方式内联编写compare方法(但我不建议对于这样一个复杂的compare方法这样做;这只是为了说明的目的):
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);
}
});
}
我有99.99%的把握,.Net Framework中默认不存在这样的东西
您的排序是非常自定义的,并且不是一种通用的排序方式,因此在.NET Framework中默认情况下不存在这样的东西。