我偶然发现了自定义IComparer实现的一些奇怪行为,似乎无法弄清楚如何纠正它并获得预期的行为。
我创建了一个静态类,为System.Guid提供了一个扩展方法,允许用指定的Int32值覆盖Guid的前4个字节。这样做是为了允许创建对高人口数据库表具有索引友好性的半序列Guid。
public static class GuidExt
{
public static Guid Sequence(this Guid obj, int index)
{
byte[] b = obj.ToByteArray();
BitConverter.GetBytes(index).CopyTo(b, 0);
return new Guid(b);
}
}
非常直接且完全符合预期。
我创建的自定义Comparer类旨在允许Guid按Guid插入的Int32部分的升序排序。实施如下:
public class GuidSequenceComparer : IComparer<Guid>
{
public int Compare(Guid x, Guid y)
{
var xBytes = x.ToByteArray();
var yBytes = y.ToByteArray();
byte[] xIndexBytes = new byte[4];
for (int i = 0; i < 4; i++)
{
xIndexBytes[i] = xBytes[0];
}
byte[] yIndexBytes = new byte[4];
for (int i = 0; i < 4; i++)
{
yIndexBytes[i] = yBytes[i];
}
var xIndex = BitConverter.ToInt32(xIndexBytes, 0);
var yIndex = BitConverter.ToInt32(yIndexBytes, 0);
return xIndex.CompareTo(yIndex);
//// The following was used to test if any sort was being performed
//// and reverses the ordering (see below paragraph)
// if (xIndex > yIndex)
// {
// return -1;
// }
// if (xIndex < yIndex)
// {
// return 1
// }
// return 0;
}
}
当我在List上使用这个自定义比较器时,它会执行排序,但它会对List对象中的索引位置进行排序!不知道为什么会这样,但我通过反转比较结果证实了这一点,如注释部分所示,它只是颠倒了List的顺序,不是基于Guid中的int值,而是基于现有的位置在列表中。我完全不知道为什么会这样。
以下是我在控制台应用中使用的简单测试,如果您想尝试重新创建行为。
List<Guid> guidList = new List<Guid>();
guidList.Add(Guid.NewGuid().Sequence(5));
guidList.Add(Guid.NewGuid().Sequence(3));
guidList.Add(Guid.NewGuid().Sequence(8));
guidList.Add(Guid.NewGuid().Sequence(1));
Console.WriteLine("unsorted:");
foreach (Guid item in guidList)
{
Console.WriteLine(item);
}
guidList.Sort(new GuidSequenceComparer());
Console.WriteLine("sorted:");
foreach (Guid item in guidList)
{
Console.WriteLine(item);
}
Console.ReadLine();
答案 0 :(得分:3)
在您的比较器中,我认为该行
xIndexBytes[i] = xBytes[0];
应该是
xIndexBytes[i] = xBytes[i];
不确定这是不是问题,但这正是我不时发生的事情: - )
用户@Magnus打败了我,但你可以避免使用Array.Copy
功能的复制循环,如下所示:
Array.Copy(xBytes, 0, xIndexBytes, 0, 4);
答案 1 :(得分:3)
我会优化您的比较
public int Compare(Guid x, Guid y)
{
var xBytes = x.ToByteArray();
var yBytes = y.ToByteArray();
int result = 0;
for (int i = 0; i < 4; i++)
{
var result = xBytes[i].CompareTo(yBytes[i]);
if (result != 0)
{
break;
}
}
return result;
}
Buffer.BlockCopy
比Array.Copy
快一点,
public static Guid Sequence(this Guid source, int index)
{
var buffer = source.ToByteArray();
Buffer.BlockCopy(BitConvertor.GetBytes(index), 0, buffer, 0, sizeof(int));
return new Guid(buffer);
}
或者如何,不需要复制
public static Guid Sequence(this Guid source, int index)
{
var buffer = source.ToByteArray();
return new Guid(
index,
BitConvertor.ToInt16(buffer, 4),
BitConvertor.ToInt16(buffer, 6),
buffer[8],
buffer[9],
buffer[10],
buffer[11],
buffer[12],
buffer[13],
buffer[14],
buffer[15]);
}
答案 2 :(得分:3)
您可能需要考虑将扩展方法调整为:
public static class GuidExt
{
public static Guid Sequence(this Guid g, int sequenceNum)
{
var bytes = g.ToByteArray();
BitConverter.GetBytes(sequenceNum).CopyTo(bytes, 0);
return new Guid(bytes);
}
public static int GetSequenceNum(this Guid g)
{
return BitConverter.ToInt32(g.ToByteArray(), 0);
}
}
你的方法中有一些你不需要的额外回转。有趣的是,这是Jalayn发现的错误的根源。
然后,您可以更改比较器以执行更简单的操作:
public int Compare(Guid x, Guid y)
{
return x.GetSequenceNum().CompareTo(y.GetSequenceNum());
}
希望这有帮助。