C# 由于某些原因,自定义IComparer实现未按预期排序
我偶然发现了自定义IComparer实现的一些奇怪行为,似乎不知道如何纠正它并获得预期的行为C# 由于某些原因,自定义IComparer实现未按预期排序,c#,.net,generics,C#,.net,Generics,我偶然发现了自定义IComparer实现的一些奇怪行为,似乎不知道如何纠正它并获得预期的行为 List<Guid> guidList = new List<Guid>(); guidList.Add(Guid.NewGuid().Sequence(5)); guidList.Add(Guid.NewGuid().Sequence(3)); guidList.Add(Guid.NewGuid().Sequen
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();
我创建了一个静态类,为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);
}
}
非常直截了当,工作完全符合预期
我创建的自定义比较器类旨在允许按照Guid插入的Int32部分的升序对Guid进行排序。实施情况如下:
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;
}
}
公共类GuidSequenceComparer:IComparer
{
公共整数比较(Guid x,Guid y)
{
var xBytes=x.ToByteArray();
var yBytes=y.ToByteArray();
字节[]xIndexBytes=新字节[4];
对于(int i=0;i<4;i++)
{
xIndexBytes[i]=xBytes[0];
}
字节[]yIndexBytes=新字节[4];
对于(int i=0;i<4;i++)
{
yIndexBytes[i]=yBytes[i];
}
var xIndex=BitConverter.ToInt32(xIndexBytes,0);
var yIndex=BitConverter.ToInt32(yIndexBytes,0);
返回xIndex.CompareTo(yIndex);
////以下内容用于测试是否正在执行任何排序
////并反转顺序(参见下面的段落)
//如果(xIndex>yIndex)
// {
//返回-1;
// }
//if(xIndex
当我在列表上使用这个自定义比较器时,它确实执行排序,但它在列表对象中对索引位置进行排序!不知道为什么会发生这种情况,但我通过颠倒比较结果(如注释掉的部分所示)来确认,它只是颠倒了列表的顺序,不是基于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();
List guidList=newlist();
添加(Guid.NewGuid().Sequence(5));
添加(Guid.NewGuid().Sequence(3));
添加(Guid.NewGuid().Sequence(8));
guidList.Add(Guid.NewGuid().Sequence(1));
Console.WriteLine(“未排序:”);
foreach(guidList中的Guid项)
{
控制台写入线(项目);
}
Sort(新的GuidSequenceComparer());
Console.WriteLine(“排序:”);
foreach(guidList中的Guid项)
{
控制台写入线(项目);
}
Console.ReadLine();
在您的比较器中,我认为
xIndexBytes[i] = xBytes[0];
应该是
xIndexBytes[i] = xBytes[i];
不确定这是否是问题所在,但这正是我时常遇到的事情:-)
User@Magnus击败了我,但您可以使用该函数避免复制循环,如下所示:
Array.Copy(xBytes, 0, xIndexBytes, 0, 4);
我会优化你的比较
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;
}
或者这个怎么样,不需要复制
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]);
}
您可能想考虑将扩展方法调整为:
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());
}
希望这有帮助。标识列有什么问题?
xBytes[0]
在循环中似乎不正确。哈哈,是的,我完全忽略了这一点。这就是问题所在,现在一切正常。啊,孩子,深夜的代码狂欢…@MikeJohnson你也可以写xBytes.CopyTo(xIndexBytes,0)
并跳过循环。@Magnus提出了提高性能的极好建议。谢谢。哈哈哇。。。非常感谢。去喝杯咖啡,羞愧地垂下头。。。没问题,很乐意帮助:-)+1非常优雅的解决方案。我喜欢这些建议。清除了相当一部分不必要的开销。谢谢:)