public static int CompareTo(Guid a, Guid b)
{
var spanA = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref a, 1));
var spanB = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref b, 1));
return spanA.SequenceCompareTo(spanB);
}
public static Guid CreateVersion7(DateTimeOffset timestamp)
{
// NewGuid uses CoCreateGuid on Windows and Interop.GetCryptographicallySecureRandomBytes on Unix to get cryptographically-secure random bytes.
// And while CoCreateGuid only generates 122 bits of randomness, the other 6 bits being for the version / variant
// fields, this method also needs those bits to be non-random, so we can just use NewGuid for efficiency.
Guid result = NewGuid();
long unix_ts_ms = timestamp.ToUnixTimeMilliseconds();
Unsafe.AsRef(in result._a) = (int)(unix_ts_ms >> 16);
Unsafe.AsRef(in result._b) = (short)(unix_ts_ms);
Unsafe.AsRef(in result._c) = (short)((result._c & ~VersionMask) | Version7Value);
Unsafe.AsRef(in result._d) = (byte)((result._d & ~Variant10xxMask) | Variant10xxValue);
return result;
}
根据RFC 9562,UUIDv7 应该仅按原始字节排序,而不是按照旧的 32 位、2x 16 位,然后按相反顺序对其余部分进行排序。
在 C# 中,您可以使用 来执行此操作,
SequenceCompareTo
您Span
可以使用 来创建MemoryMarshal.CreateSpan
。您可以
IComparer
使用 来为此创建一个Comparer<Guid>.Create
。dotnetfiddle
除非实现中带有可选的 counters ,否则仍然不能保证获得连续的 ID。.NET似乎没有这样做。源代码 (缩写)如下:
您可以看到只有
version
和var
字段被填充。获得保证单调的 UUIDv7 的一种方法是保留自己的日期计数器并手动调整它,正如 RFC 中所建议的那样。
CreateVersion7
接受我们可以传入的时间戳。它使用 Unix 转换为毫秒,因此我们需要基于此进行调整。您可能想要在这里添加线程安全。