我有一个通用的
List<MyClass>
其中
MyClass
有一个属性 InvoiceNumber
,其中包含以下值:
200906/1
200906/2
..
200906/10
200906/11
200906/12
我的列表绑定到一个
BindingList<T>
支持使用 linq 排序:
protected override void ApplySortCore(
PropertyDescriptor property, ListSortDirection direction)
{
_sortProperty = property;
_sortDirection = direction;
var items = this.Items;
switch (direction)
{
case ListSortDirection.Ascending:
items = items.OrderByDescending(x => property.GetValue(x)).ToList();
break;
case ListSortDirection.Descending:
items = items.OrderByDescending(x => property.GetValue(x)).ToList();
break;
}
this.Items = items;
}
但是默认比较器的排序(如假设的那样)如下:
200906/1
200906/10
200906/11
200906/12
200906/2
在这种情况下,这很令人讨厌。
现在我想用我自己的
IComparer<T>
来实现这个。看起来像这样:
public class MyComparer : IComparer<Object>
{
public int Compare(Object stringA, Object stringB)
{
String[] valueA = stringA.ToString().Split('/');
String[] valueB = stringB.ToString().Split('/');
if(valueA .Length != 2 || valueB .Length != 2)
return String.Compare(stringA.ToString(), stringB.ToString());
if (valueA[0] == valueB[0])
{
return String.Compare(valueA[1], valueB[1]);
}
else
{
return String.Compare(valueA[0], valueB[0]);
}
}
}
并更改了
ApplySortCore
代码以使用此 IComparer
:
case ListSortDirection.Ascending:
MyComparer comparer = new MyComparer();
items = items.OrderByDescending(
x => property.GetValue(x), comparer).ToList();
break;
当我调试代码时,我发现
MyComparer.Compare(object, object)
被多次调用,并为比较方法返回正确的值 (-1, 0, 1)。
但是我的列表仍然以“错误”的方式排序。我错过了什么吗?我不知道。
我觉得你的比较器看起来不对。您仍然只是按照默认的文本顺序进行排序。当然,您希望解析这两个数字并基于此进行排序:
public int Compare(Object stringA, Object stringB)
{
string[] valueA = stringA.ToString().Split('/');
string[] valueB = stringB.ToString().Split('/');
if (valueA.Length != 2 || valueB.Length != 2)
{
stringA.ToString().CompareTo(stringB.ToString());
}
// Note: do error checking and consider i18n issues too :)
if (valueA[0] == valueB[0])
{
return int.Parse(valueA[1]).CompareTo(int.Parse(valueB[1]));
}
else
{
return int.Parse(valueA[0]).CompareTo(int.Parse(valueB[0]));
}
}
(请注意,这与您的问题不一致,即您已经调试并验证了 Compare 返回正确的值 - 但恐怕我怀疑这方面的人为错误。)
此外,Sven 是对的 - 更改
items
的值根本不会更改您的绑定列表。您应该添加:
this.Items = items;
在方法的底部。
我遇到了一般自然排序的问题,并在此处发布了解决方案:
public class NaturalSortComparer<T> : IComparer<string>, IDisposable
{
private bool isAscending;
public NaturalSortComparer(bool inAscendingOrder = true)
{
this.isAscending = inAscendingOrder;
}
#region IComparer<string> Members
public int Compare(string x, string y)
{
throw new NotImplementedException();
}
#endregion
#region IComparer<string> Members
int IComparer<string>.Compare(string x, string y)
{
if (x == y)
return 0;
string[] x1, y1;
if (!table.TryGetValue(x, out x1))
{
x1 = Regex.Split(x.Replace(" ", ""), "([0-9]+)");
table.Add(x, x1);
}
if (!table.TryGetValue(y, out y1))
{
y1 = Regex.Split(y.Replace(" ", ""), "([0-9]+)");
table.Add(y, y1);
}
int returnVal;
for (int i = 0; i < x1.Length && i < y1.Length; i++)
{
if (x1[i] != y1[i])
{
returnVal = PartCompare(x1[i], y1[i]);
return isAscending ? returnVal : -returnVal;
}
}
if (y1.Length > x1.Length)
{
returnVal = 1;
}
else if (x1.Length > y1.Length)
{
returnVal = -1;
}
else
{
returnVal = 0;
}
return isAscending ? returnVal : -returnVal;
}
private static int PartCompare(string left, string right)
{
int x, y;
if (!int.TryParse(left, out x))
return left.CompareTo(right);
if (!int.TryParse(right, out y))
return left.CompareTo(right);
return x.CompareTo(y);
}
#endregion
private Dictionary<string, string[]> table = new Dictionary<string, string[]>();
public void Dispose()
{
table.Clear();
table = null;
}
}
我们不能这样做吗:
public class MyComparer : IComparer<string>
{
public int Compare(string stringA, string stringB)
{
string small = stringA;
string big = stringB;
if (stringA.Length > stringB.Length)
{
small = stringB;
big = stringA;
}
else if (stringA.Length < stringB.Length)
{
small = stringA;
big = stringB;
}
for (int j = 0; j < small.Length; j++)
{
if (Convert.ToInt32(small[j]) > Convert.ToInt32(big[j])) return -1;
if (Convert.ToInt32(small[j]) < Convert.ToInt32(big[j])) return 1;
}
//big is indeed bigger
if (big.Length > small.Length) return 1;
//finally they are smae
return 0;
}
}
用途:
string[] inputStrings = {"_abc*&","#almnp","abc" };
//string[] inputStrings = { "#", "_", "_a", "@", "_" };
MyComparer computer = new MyComparer();
var kola = inputStrings.OrderBy(x => x, new MyComparer()).ToArray();
这与:
相同 Array.Sort(inputStrings, StringComparer.Ordinal);
您可以使用 Alphanum 算法:
(...)
items.OrderBy(x => property.GetValue(x), new AlphanumComparator())
(...)
AlphanumComparator
/*
* The Alphanum Algorithm is an improved sorting algorithm for strings
* containing numbers. Instead of sorting numbers in ASCII order like
* a standard sort, this algorithm sorts numbers in numeric order.
*
* The Alphanum Algorithm is discussed at http://www.DaveKoelle.com
*
* Based on the Java implementation of Dave Koelle's Alphanum algorithm.
* Contributed by Jonathan Ruckwood <[email protected]>
*
* Adapted by Dominik Hurnaus <[email protected]> to
* - correctly sort words where one word starts with another word
* - have slightly better performance
*
* Released under the MIT License - https://opensource.org/licenses/MIT
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
* USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
using System;
using System.Collections;
using System.Text;
/*
* Please compare against the latest Java version at http://www.DaveKoelle.com
* to see the most recent modifications
*/
namespace AlphanumComparator
{
public class AlphanumComparator : IComparer
{
private enum ChunkType {Alphanumeric, Numeric};
private bool InChunk(char ch, char otherCh)
{
ChunkType type = ChunkType.Alphanumeric;
if (char.IsDigit(otherCh))
{
type = ChunkType.Numeric;
}
if ((type == ChunkType.Alphanumeric && char.IsDigit(ch))
|| (type == ChunkType.Numeric && !char.IsDigit(ch)))
{
return false;
}
return true;
}
public int Compare(object x, object y)
{
String s1 = x as string;
String s2 = y as string;
if (s1 == null || s2 == null)
{
return 0;
}
int thisMarker = 0, thisNumericChunk = 0;
int thatMarker = 0, thatNumericChunk = 0;
while ((thisMarker < s1.Length) || (thatMarker < s2.Length))
{
if (thisMarker >= s1.Length)
{
return -1;
}
else if (thatMarker >= s2.Length)
{
return 1;
}
char thisCh = s1[thisMarker];
char thatCh = s2[thatMarker];
StringBuilder thisChunk = new StringBuilder();
StringBuilder thatChunk = new StringBuilder();
while ((thisMarker < s1.Length) && (thisChunk.Length==0 ||InChunk(thisCh, thisChunk[0])))
{
thisChunk.Append(thisCh);
thisMarker++;
if (thisMarker < s1.Length)
{
thisCh = s1[thisMarker];
}
}
while ((thatMarker < s2.Length) && (thatChunk.Length==0 ||InChunk(thatCh, thatChunk[0])))
{
thatChunk.Append(thatCh);
thatMarker++;
if (thatMarker < s2.Length)
{
thatCh = s2[thatMarker];
}
}
int result = 0;
// If both chunks contain numeric characters, sort them numerically
if (char.IsDigit(thisChunk[0]) && char.IsDigit(thatChunk[0]))
{
thisNumericChunk = Convert.ToInt32(thisChunk.ToString());
thatNumericChunk = Convert.ToInt32(thatChunk.ToString());
if (thisNumericChunk < thatNumericChunk)
{
result = -1;
}
if (thisNumericChunk > thatNumericChunk)
{
result = 1;
}
}
else
{
result = thisChunk.ToString().CompareTo(thatChunk.ToString());
}
if (result != 0)
{
return result;
}
}
return 0;
}
}
}
排序列表仅绑定到局部变量项目,而不绑定到绑定列表的 Items 属性,因此它保持未排序状态。
[编辑]基本上,您只是丢弃了排序工作的结果;-)
有一个 Windows API:
public sealed class NaturalStringComparer : IComparer<string>
{
readonly ListSortDirection _direction;
public NaturalStringComparer(ListSortDirection direction = ListSortDirection.Ascending)
{
_direction = direction;
}
public int Compare(string x, string y)
{
return this._direction == ListSortDirection.Ascending ? NaturalStringComparer.SafeNativeMethods.StrCmpLogicalW(x, y) : NaturalStringComparer.SafeNativeMethods.StrCmpLogicalW(y, x);
}
[SuppressUnmanagedCodeSecurity]
static class SafeNativeMethods
{
[DllImport("shlwapi.dll", CharSet = CharSet.Unicode)]
public static extern int StrCmpLogicalW(string psz1, string psz2);
}
}