如何将Long类型转换为Guid类型,反之亦然?

8
这真的可能吗?我甚至不认为这是可能的,但我看到了一些尝试实现它的代码。然而,我的单元测试显示它并没有起作用。我确实看到了一些类似的想法: 仅供澄清,Guid创建在我的控制范围之外;因此,我不能只将值存储在其中一个Guid集中。
7个回答

29
一个GUID永远不能适应一个long类型,因为GUID的大小是16个字节,而long类型的大小是8个字节。即使这8个字节是零,你也不能快乐地将它们从GUID中丢弃掉! :-)
一个Guid只是一个128位的数据结构,而long是一个64位的数据结构...
这意味着只要最高的64位是零,我们就可以安全地丢弃它们,并在以后重建它们,因为在这种情况下零并不是有意义的数据。所以是数据损失,但在某些情况下,这种数据损失是可以重建的。(想象一下压缩的概念)
这和你执行的操作没有什么不同:
long x = 5000;
//Converting 64 bit data structure into a 32 bit, discarding the upper 32 bits.
int y = (int)x;
//Converting 32 bit data structure into a 64 bit.
long z = (long)y;

这些显式转换没有内置到系统中,因此必须以不同的方式完成。

public static Guid ToGuid(long value)
{
    byte[] guidData = new byte[16];
    Array.Copy(BitConverter.GetBytes(value), guidData, 8);
    return new Guid(guidData);
}

public static long ToLong(Guid guid)
{
    if (BitConverter.ToInt64(guid.ToByteArray(), 8) != 0)
        throw new OverflowException("Value was either too large or too small for an Int64.");
    return BitConverter.ToInt64(guid.ToByteArray(), 0);
}

显然,这个方法不应该用于通用的GUID,但只要GUID只有64位“实际数据”,就可以安全使用,确保这一点的最好方式是除非使用long创建了它们,否则不要在GUID上使用它。

作为预防措施,我添加了一个OverflowException,以防GUID在其最后64位存储任何非0值,因为生成的long不能转换回相同的GUID。

因此...

只要创建的GUID适合long,则可能吗?只有当超过long的指定大小时才出现问题,对吗?

是的...显然,GUID的相应long值不容易读取,以下是一些示例(long = guid):

1.340 = 0000053c-0000-0000-0000-000000000000
98.605 = 0001812d-0000-0000-0000-000000000000
149.957 = 000249c5-0000-0000-0000-000000000000
218.125.225 = 0d0053a9-0000-0000-0000-000000000000
4.249.596.910 = fd4bb3ee-0000-0000-0000-000000000000
23.139.913.545 = 633f0f49-0005-0000-0000-000000000000
9.223.372.036.854.775.807 = ffffffff-ffff-7fff-0000-000000000000
-9.223.372.036.854.775.808 = 00000000-0000-8000-0000-000000000000
-1 = ffffffff-ffff-ffff-0000-000000000000

是的,这些转换都是双向的,并且适用于您可以想到的任何长整型值...

但是因为您声明:

The Guid creation is outside of my control.

实际上极不可能实现这一点,相反,您拥有的 Guid 很可能是使用任何常见算法生成的,这将使 Guid 在此场景中无法使用。


现在是否有任何意义去做这个?...这需要对特定情况有深入了解,这是一个完全不同的讨论...


天啊,为什么这个答案没有被采纳呢?它非常详尽。 - Irwin

9

GUID是16字节长的。"长"通常被理解为64位(即8字节)长。你不能在不丢失或提供额外数据的情况下在它们之间进行转换。


只要创建的 GUID 符合 long 类型,就可以吗?只有当超过 long 类型指定的大小时才会出现问题,对吗? - kevindaub
一个GUID永远无法适应long类型,因为GUID的大小是16个字节,而long的大小是8个字节。即使这8个字节是零,你也不能轻易地丢弃GUID中的它们! :-) - CesarGon
1
你是绝对正确的。我在那个评论中没有好好思考 :) - kevindaub
你是正确的。两个注意点:C#中的ulong和C语言中的"long long int"都恰好是64位,因此一个Guid可以“刚好”放入其中两个。 - ntg

4

我知道这个问题非常老,但它是谷歌的第一个搜索结果,我想提供我采用的答案。

Guid 不像其他已经说明的那样是 Int64,而是两个 UInt64。

我编写了一个 UInt128 类,其中包含一个高位和一个低位的 UInt64。从那里开始,你只需要在它们之间进行转换:

    public static UInt128 ToUInt128(this Guid g)
    {
        var array = g.ToByteArray();
        var hi = BitConverter.ToUInt64(array, 8);
        var lo = BitConverter.ToUInt64(array, 0);
        return new UInt128(hi, lo);
    }

    public static Guid ToGuid(this UInt128 i)
    {
        byte[] data = new byte[16];
        Array.Copy(BitConverter.GetBytes(i.hi), 0, data, 8, 8);
        Array.Copy(BitConverter.GetBytes(i.lo), 0, data, 0, 8);
        return new Guid(data);
    }

也许这会对某些人有所帮助!

非常有用,感谢。我也使用了hiLong和loLong,因为我也有这个需求;现在公共成员是hiUlong、loUlong和对应的Long类型。 - Marcelo Scofano Diniz
这是一个有用的方法。谢谢分享。 - digital_jedi

0

你可以将一个长整型(64位)映射成一个GUID(128位)。
但是你不能将一个GUID(128位)映射成一个长整型(64位)。
实际上,你需要的是将GUID映射成uint128(或int128)。
当然,将长整型映射成GUID时,需要在uint128的高部分补零。

你需要考虑的是GUID字节的特殊排序方式(每个字节的优先顺序),这样就可以像长整型(long=int64)一样进行范围比较。

为此,你需要查看以下回答:
SQL Server GUID sort algorithm

如何将long映射为ulong,将int映射为uint,请参见:
Mapping a ulong to a long in C#?

如何将UInt128转换为GUID,请参见GitHub上UInt128.cs中的ToGuidBytes()和ToGuid(),以及将长整型转换为UInt128,请参考UInt128.cs的构造函数。

public byte[] ToGuidBytes()
{
    // byte[] ba = this.ToByteArrayLowToHighEnsured();
    byte[] ba = this.ToByteArray(); // Fast 

    int[] guidByteOrder = new int[16] // 16 Bytes = 128 Bit 
       {10, 11, 12, 13, 14, 15,  8,  9,  6,  7,  4,  5,  0,  1,  2,  3};
    // {00, 01, 02, 03, 04, 05, 06, 07, 08, 09, 10, 11, 12, 13, 14, 15}

    byte[] guidBytes = new byte[16];
    for (int i = 0; i < 16; ++i)
    {
        guidBytes[guidByteOrder[15 - i]] = ba[i];
    } // Next i 
    guidByteOrder = null;
    ba = null;

    return guidBytes;
} // End Function ToGuidBytes 


public System.Guid ToGuid()
{
    return new System.Guid(this.ToGuidBytes());
}

关于System.Guid.NewGuid()请参考“NextUInt128”:

以下是一些测试来验证/检查它的工作情况: BigIntUidTests.cs

Caveat emptor:
我不知道小端/大端机器会如何影响结果。


0
只要您需要操作不超过Guid大小的正整数值,就可以像这样将数字转换为Guid:
var guid = new Guid(ulong.MaxValue.ToString("00000000-0000-0000-0000-000000000000"));

并将其转换回来,如下:

var uint64 = Convert.ToUInt64(guid.ToString("N"))

在其他情况下最好使用如上所述的字节转换。可以在此处检查示例:https://dotnetfiddle.net/Zx7faI

0

您可以使用Guid构造函数,这是一个简单但并非完美的示例:

Guid g1;
long g2;

// {00000001-0000-0000-0000-000000000000}
g1 = new Guid(1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);

// 00000001
g2 = Convert.ToInt64(g1.ToString().Split('-')[0]);

// 1 + 5 = 6
g2 += 5;

// {00000006-0000-0000-0000-000000000000}
g1 = new Guid((uint)g2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);

第一组可以处理高达 4294967295(int)的值。如果你需要更多,那么你必须使用另一个集合。
每个集合是无符号整数的十六进制表示,所以你可以尝试不同的可能性。

0
public static Guid ToGuid(long value)
{
    byte[] bytes = new byte[16];
    BitConverter.GetBytes(value).CopyTo(bytes, 0);
    return new Guid(bytes);
}

希望这段代码能对您有所帮助!!!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接