将GUID转换为2个长整型数值,同时将2个长整型数值转换为GUID(C#)

3

Guid是一个128位的结构,long是Int64,因此Guid可以用于表示两个long,而两个long可以存储在Guid中。

我多次搜索了可靠的方法来执行Guid到2个long和2个long到Guid的转换,主要是为了提供一个简单的方式给外部服务提供跟踪ID。

目标是获得一种可逆的方式,在单个参数中传递2个long,并在以后解码它(当然不打算在另一端“解码”使用)。它就像外部服务的会话ID。


在将Guid视为其他内容时需要注意一点:通常有两种表达GUID的方法,涉及内部字节顺序。如果您希望与其他任何内容兼容,请务必仔细检查 - Marc Gravell
如果unsafe是一个选项,这可能只需要大约3行代码 :) - Marc Gravell
是的,考虑添加一个检查 BitConverter.IsLittleEndian 的答案之一,以确定是否需要反转字节,如果您需要确保字节始终以相同的顺序排列,无论平台如何。 - BlueMonkMN
@BlueMonkMN 如果目标是实现互操作性,我会同意你的建议,但这不是我的情况,所以我更喜欢一个更快的解决方案。然而,这也需要确保如何存储GUID以检查字节序,这一点我不太确定... - Jean
@Jean 按要求完成 :) - Marc Gravell
显示剩余2条评论
4个回答

5

注意: 这些解决方案不考虑字节序,因此结果可能因平台而异。

利用C# 7的新功能,我编写了以下工具类,将long、ulong、int、uint转换为Guid并反向转换:

public static class GuidTools
{
    public static Guid GuidFromLongs(long a, long b)
    {
        byte[] guidData = new byte[16];
        Array.Copy(BitConverter.GetBytes(a), guidData, 8);
        Array.Copy(BitConverter.GetBytes(b), 0, guidData, 8, 8);
        return new Guid(guidData);
    }

    public static (long, long) ToLongs(this Guid guid)
    {
        var bytes = guid.ToByteArray();
        var long1 = BitConverter.ToInt64(bytes, 0);
        var long2 = BitConverter.ToInt64(bytes, 8);
        return (long1, long2);
    }

    public static Guid GuidFromULongs(ulong a, ulong b)
    {
        byte[] guidData = new byte[16];
        Array.Copy(BitConverter.GetBytes(a), guidData, 8);
        Array.Copy(BitConverter.GetBytes(b), 0, guidData, 8, 8);
        return new Guid(guidData);
    }

    public static (ulong, ulong) ToULongs(this Guid guid)
    {
        var bytes = guid.ToByteArray();
        var ulong1 = BitConverter.ToUInt64(bytes, 0);
        var ulong2 = BitConverter.ToUInt64(bytes, 8);
        return (ulong1, ulong2);
    }

    public static Guid GuidFromInts(int a, int b, int c, int d)
    {
        byte[] guidData = new byte[16];
        Array.Copy(BitConverter.GetBytes(a), guidData, 4);
        Array.Copy(BitConverter.GetBytes(b), 0, guidData, 4, 4);
        Array.Copy(BitConverter.GetBytes(c), 0, guidData, 8, 4);
        Array.Copy(BitConverter.GetBytes(d), 0, guidData, 12, 4);
        return new Guid(guidData);
    }

    public static (int, int , int, int) ToInts(this Guid guid)
    {
        var bytes = guid.ToByteArray();
        var a = BitConverter.ToInt32(bytes, 0);
        var b = BitConverter.ToInt32(bytes, 4);
        var c = BitConverter.ToInt32(bytes, 8);
        var d = BitConverter.ToInt32(bytes, 12);
        return (a, b, c, d);
    }

    public static Guid GuidFromUInts(uint a, uint b, uint c, uint d)
    {
        byte[] guidData = new byte[16];
        Array.Copy(BitConverter.GetBytes(a), guidData, 4);
        Array.Copy(BitConverter.GetBytes(b), 0, guidData, 4, 4);
        Array.Copy(BitConverter.GetBytes(c), 0, guidData, 8, 4);
        Array.Copy(BitConverter.GetBytes(d), 0, guidData, 12, 4);
        return new Guid(guidData);
    }

    public static (uint, uint, uint, uint) ToUInts(this Guid guid)
    {
        var bytes = guid.ToByteArray();
        var a = BitConverter.ToUInt32(bytes, 0);
        var b = BitConverter.ToUInt32(bytes, 4);
        var c = BitConverter.ToUInt32(bytes, 8);
        var d = BitConverter.ToUInt32(bytes, 12);
        return (a, b, c, d);
    }
}

我还找到了另一个灵感来源于这里的解决方案:将System.Decimal转换为System.Guid

[StructLayout(LayoutKind.Explicit)]
struct GuidConverter
{
    [FieldOffset(0)]
    public decimal Decimal;
    [FieldOffset(0)]
    public Guid Guid;
    [FieldOffset(0)]
    public long Long1;
    [FieldOffset(8)]
    public long Long2;
}

private static GuidConverter _converter;
public static (long, long) FastGuidToLongs(this Guid guid)
{
    _converter.Guid = guid;
    return (_converter.Long1, _converter.Long2);
}
public static Guid FastLongsToGuid(long a, long b)
{
    _converter.Long1 = a;
    _converter.Long2 = b;
    return _converter.Guid;
}

3
作为一种不安全但非常高效的版本(没有 byte[] 分配,通过 BitConverter 实现):
static void Main()
{
    var g = Guid.NewGuid();
    Console.WriteLine(g);
    GuidToInt64(g, out var x, out var y);
    Console.WriteLine(x);
    Console.WriteLine(y);
    var g2 = GuidFromInt64(x, y);
    Console.WriteLine(g2);
}
public static unsafe void GuidToInt64(Guid value, out long x, out long y)
{
    long* ptr = (long*)&value;
    x = *ptr++;
    y = *ptr;
}
public static unsafe Guid GuidFromInt64(long x, long y)
{
    long* ptr = stackalloc long[2];
    ptr[0] = x;
    ptr[1] = y;
    return *(Guid*)ptr;
}

如果您不喜欢使用unsafe关键字,实际上您可以使用联合结构来完成相同的事情,但是这样会产生更多的代码,并且联合结构仍然是基本不可验证的,因此在IL级别上并没有太大好处(这只意味着您不需要“允许不安全代码”标志):

static void Main()
{
    var g = Guid.NewGuid();
    Console.WriteLine(g);

    var val = new GuidInt64(g);
    var x = val.X;
    var y = val.Y;
    Console.WriteLine(x);
    Console.WriteLine(y);

    var val2 = new GuidInt64(x, y);
    var g2 = val2.Guid;
    Console.WriteLine(g2);
}

[StructLayout(LayoutKind.Explicit)]
struct GuidInt64
{
    [FieldOffset(0)]
    private Guid _guid;
    [FieldOffset(0)]
    private long _x;
    [FieldOffset(8)]
    private long _y;

    public Guid Guid => _guid;
    public long X => _x;
    public long Y => _y;

    public GuidInt64(Guid guid)
    {
        _x = _y = 0; // to make the compiler happy
        _guid = guid;
    }
    public GuidInt64(long x, long y)
    {
        _guid = Guid.Empty;// to make the compiler happy
        _x = x;
        _y = y;
    }
}

0

以下一对方法可以实现您所需的功能:

    public static void GuidToInt16(Guid guidToConvert, out long guidAsLong1, out long guidAsLong2)
    {
        byte[] guidByteArray = guidToConvert.ToByteArray();
        var segment1 = new ArraySegment<byte>(guidByteArray, 0, 8);
        var segment2 = new ArraySegment<byte>(guidByteArray, 8, 8);
        guidAsLong1 = BitConverter.ToInt64(segment1.ToArray(), 0);
        guidAsLong2 = BitConverter.ToInt64(segment2.ToArray(), 0);
    }

    public static Guid Int16ToGuid(long guidAsLong1, long guidAsLong2)
    {
        var segment1 = BitConverter.GetBytes(guidAsLong1);
        var segment2 = BitConverter.GetBytes(guidAsLong2);
        return new Guid(segment1.Concat(segment2).ToArray());
    }

以及可能的使用方法:

        Guid guidToConvert = new Guid("cbd5bb87-a249-49ac-8b06-87c124205b99");
        long guidAsLong1, guidAsLong2;

        GuidToInt16(guidToConvert, out guidAsLong1, out guidAsLong2);
        Console.WriteLine(guidAsLong1 + " " + guidAsLong2);

        Guid guidConvertedBack = Int16ToGuid(guidAsLong1, guidAsLong2);
        Console.WriteLine(guidConvertedBack);

        Console.ReadKey();

0

我的解决方案应该有助于理解二进制操作的整个过程:

    class Program
{
    public static Guid LongsToGuid(long l1, long l2)
    {
        var a = (int)l1;
        var b = (short)(l1 >> 32);
        var c = (short)(l1 >> 48);

        var d = (byte)l2;
        var e = (byte)(l2 >> 8);
        var f = (byte)(l2 >> 16);
        var g = (byte)(l2 >> 24);
        var h = (byte)(l2 >> 32);
        var i = (byte)(l2 >> 40);
        var j = (byte)(l2 >> 48);
        var k = (byte)(l2 >> 56);

        return new Guid(a, b, c, d, e, f, g, h, i, j, k);
    }

    public static long BytesToLong(byte[] bytes, int start, int end)
    {
        long toReturn = 0;

        for (var i = start; i < end; i++)
        {
            toReturn |= ((long)bytes[i]) << (8 * i);
        }

        return toReturn;
    }

    static void Main(string[] args)
    {
        var l1 = long.MinValue;
        var l2 = long.MaxValue;

        var guid = LongsToGuid(l1, l2);
        var guidBytes = guid.ToByteArray();

        var readL1 = BytesToLong(guidBytes, 0, 8);
        var readL2 = BytesToLong(guidBytes, 8, 16);

        Console.WriteLine(l1 == readL1);
        Console.WriteLine(l2 == readL2);

        Console.ReadKey();
    }
}

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