我有一个需要保证唯一性的GUID变量。虽然从统计学的角度来看,任何GUID都应该被视为唯一的,但由于开发/测试环境的原因,同样的值可能会出现多次。因此,当出现这种情况时,我想“增加”GUID的值,而不仅仅是创建一个全新的。目前似乎没有简单的方法来实现这个目标。我找到了一种hack方式,可以作为一种可能的答案,但我想要一种更简洁的解决方案。
我有一个需要保证唯一性的GUID变量。虽然从统计学的角度来看,任何GUID都应该被视为唯一的,但由于开发/测试环境的原因,同样的值可能会出现多次。因此,当出现这种情况时,我想“增加”GUID的值,而不仅仅是创建一个全新的。目前似乎没有简单的方法来实现这个目标。我找到了一种hack方式,可以作为一种可能的答案,但我想要一种更简洁的解决方案。
static class GuidExtensions
{
private static readonly int[] _guidByteOrder =
new[] { 15, 14, 13, 12, 11, 10, 9, 8, 6, 7, 4, 5, 0, 1, 2, 3 };
public static Guid Increment(this Guid guid)
{
var bytes = guid.ToByteArray();
bool carry = true;
for (int i = 0; i < _guidByteOrder.Length && carry; i++)
{
int index = _guidByteOrder[i];
byte oldValue = bytes[index]++;
carry = oldValue > bytes[index];
}
return new Guid(bytes);
}
}
编辑:现在字节顺序正确。
/checked
选项进行编译?默认情况下,整数算术运算是不受检查的。 - Thomas Levesque感谢Thomas Levesque提供的字节顺序,这里有一个漂亮的LINQ实现:
static int[] byteOrder = { 15, 14, 13, 12, 11, 10, 9, 8, 6, 7, 4, 5, 0, 1, 2, 3 };
static Guid NextGuid(Guid guid)
{
var bytes = guid.ToByteArray();
var canIncrement = byteOrder.Any(i => ++bytes[i] != 0);
return new Guid(canIncrement ? bytes : new byte[16]);
}
Guid.Empty
。bytes
的副本而不是依次调用每个 GUID 上的 ToByteArray
,那么效率会更高。checked
上下文中运行。VS 默认情况下不是,所以我没有费心。 - Rawlingpublic static Guid Increment(this Guid value)
{
var bytes = value.ToByteArray();
// Note that the order of bytes in the returned byte array is different from the string representation of a Guid value.
// Guid: 00112233-4455-6677-8899-aabbccddeeff
// byte array: 33 22 11 00 55 44 77 66 88 99 AA BB CC DD EE FF
// So the byte order of the following indexes indicates the true low-to-high sequence
if (++bytes[15] == 0) if (++bytes[14] == 0) if (++bytes[13] == 0) if (++bytes[12] == 0) if (++bytes[11] == 0) if (++bytes[10] == 0) // normal order
if (++bytes[9] == 0) if (++bytes[8] == 0) // normal order
if (++bytes[6] == 0) if (++bytes[7] == 0) // reverse order
if (++bytes[5] == 0) if (++bytes[4] == 0) // reverse order
if (++bytes[3] == 0) if (++bytes[2] == 0) if (++bytes[1] == 0) { ++bytes[0]; } // reverse order
return new Guid(bytes);
}
编辑:这是我最终使用的代码;感谢上面的答案提供了一般技术,但是如果没有"unchecked"子句,在某些情况下两者都会抛出异常。但我也试图尽可能使下面的内容易读。
private static int[] _guidByteOrder = { 15, 14, 13, 12, 11, 10, 9, 8, 6, 7, 4, 5, 0, 1, 2, 3 };
public static Guid NextGuid(this Guid guid)
{
var bytes = guid.ToByteArray();
for (int i = 0; i < 16; i++)
{
var iByte = _guidByteOrder[i];
unchecked { bytes[iByte] += 1; }
if (bytes[iByte] != 0)
return new Guid(bytes);
}
return Guid.Empty;
}
if
语句链很棒。 - RawlingBigInteger
进行递增将会导致明显正确且可能看起来很好的代码。 - Alexei Levenkov有序字符串的验证解决方案:
private static Guid Increment(Guid guid)
{
byte[] bytes = guid.ToByteArray();
byte[] order = { 15, 14, 13, 12, 11, 10, 9, 8, 6, 7, 4, 5, 0, 1, 2, 3 };
for (int i = 0; i < 16; i++)
{
if (bytes[order[i]] == byte.MaxValue)
{
bytes[order[i]] = 0;
}
else
{
bytes[order[i]]++;
return new Guid(bytes);
}
}
throw new OverflowException("Congratulations you are one in a billion billion billion billion etc...");
}
private static Guid IncrementProof(Guid guid, int start, int end)
{
byte[] bytes = guid.ToByteArray();
byte[] order = { 15, 14, 13, 12, 11, 10, 9, 8, 6, 7, 4, 5, 0, 1, 2, 3 };
for (int i = start; i < end; i++)
{
if (bytes[order[i]] == byte.MaxValue)
{
bytes[order[i]] = 0;
}
else
{
bytes[order[i]]++;
return new Guid(bytes);
}
}
throw new OverflowException("Congratulations you are one in a billion billion billion billion etc...");
}
static void Main(string[] args)
{
Guid temp = new Guid();
for (int j = 0; j < 16; j++)
{
for (int i = 0; i < 255; i++)
{
Console.WriteLine(temp.ToString());
temp = IncrementProof(temp, j, j + 1);
}
}
}