C#如何生成GUID?

52

C#中如何生成GUID?


3
投票以关闭为重复问题 如何生成.NET 4 GUID?。我认为原则上它不是完美的重复(特别是现在存在.NET Core,并且不清楚重复中的答案是否适用于非Windows操作系统上的.NET Core),但当前这个问题没有一个接近正确的答案,而链接中的问题却有。 - Mark Amery
我同意这个问题没有一个正确的答案。任何来这里寻找实际有用信息的人请参见 https://dev59.com/CHE85IYBdhLWcg3wejfn#2757969 - NH.
@MarkAmery,两个问题都不是重复的。这个问题比你提到的那个早了6个月,另一个问题则是针对版本4的特定问题。.NET 4 GUIDs是如何生成的?有很好的答案,但为了全面了解,应该考虑RFC 4122 - Chiramisu
@Chiramisu “这个问题早在6个月前就被问过了” - 是的,但这并不重要。将一个较旧的问题作为一个较新的问题的重复关闭是完全合法的。当决定关闭方向时,我们应该主要考虑哪个问题对于搜索者来说更有用;年龄并不重要。 - Mark Amery
1
虽然我认为您的回复合理,但我想保持适当的怀疑,并发现了这个答案与您的观点一致,以及帮助部分中指向同样观点的文章。就此而言,我也会投票表示重复。;) - Chiramisu
9个回答

66

这里有一篇非常好的文章在 Raymond Chen 的博客上,描述了GUID是如何生成的,特别是为什么GUID的子串不能保证是唯一的。

基本上,GUID是使用以下组合生成的:

  • 用于生成GUID的计算机的MAC地址(因此,在不重复使用MAC地址的情况下,在不同的计算机上生成的GUID是唯一的)
  • 时间戳(因此,在同一台计算机上的不同时间生成的GUID是唯一的)
  • 额外的“紧急唯一标识符位”(这些用于确保在同一台计算机上几乎同时生成的GUID是唯一的)
  • 算法标识符(以便使用不同算法生成的GUID是唯一的)

然而,这只是生成GUID所使用的1个特定算法(尽管我相信这是 .NET框架所使用的算法),并且不是.NET框架所使用的算法


6
.NET使用Windows算法生成随机数(版本4 GUID),该算法自Windows 2000开始使用:https://dev59.com/CHE85IYBdhLWcg3wejfn#2757969 - Paul B.
5
这个答案基本上通过最后一句话末尾的链接摧毁了整个前提,并承认不是对所提问题的回答。 - Mark Amery

14

4
这篇文章讨论了几种生成全局唯一标识符(GUID)的算法。.NET 实际使用哪个算法? - jpmc26

11
一个 .Net 的 System.Guid 只是一个 128 位的整数(16 字节)。数字和字母与此无关。您可以使用 ToString() 方法查看 Guid 的各种“人类可读”的版本,其中包括数字 0-9 和字母 A-F(表示十六进制值),但输出方式完全由您决定。

29
这个答案是错误的,因为它回答了“GUID是什么?”的问题,但真正的问题是“C#如何生成GUID?”。因此,@Justin提供的答案才是正确的。 - Ognyan Dimitrov
@OgnyanDimitrov-最初的问题因为英语表达不清而很模糊。听起来像是请求与GUID的字符串表示有关。你可以查看问题的历史记录。可以说,由于所做的编辑和此答案被提问者接受的事实,原始问题的含义已经改变了。我会将原始问题添加到我的答案中,以防有人使用这个页面寻找原始问题。 - Dave Mateer
请不要生气。我写这篇文章并非是为了责备或嘲笑您。您现在可以编辑您的回答,不是因为其他原因,而是为了提高质量,即使问题一开始就很模糊。下一个通过搜索结果找到这个问题的人将会得到更好的答案。我相信您经验丰富、知识渊博,我并不是想贬低您的回答。 - Ognyan Dimitrov

2
这要看情况。在Unix的.NET Core中,GUID是通过创建128位的随机数并执行一些位运算来生成的。在Windows的.NET Core和.NET Framework中,它会调用Windows函数UuidCreate生成GUID(因此完全取决于您的Windows版本如何生成)。对于Unix和最新版本的Windows,您会注意到一个十六进制数字始终为4。这是因为这是Uuid 4的版本号,这意味着它们是使用随机字节生成的。以前生成GUID的方法包括时间戳和MAC地址,但这成为了攻击向量,因为它向最终用户提供了有关系统的信息,并帮助他们更容易地预测未来的GUID。

这是我今天找到的最好的答案之一。 - Urasquirrel

2

除了RFC中提到的“随机16字节”之外,还有其他形式的GUID。一些微软产品(例如SQL Server)可以选择生成这些“顺序GUID”,它们基于“系统中第一个网络卡的MAC地址+基于系统时间的递增计数器”的组合。

这些“顺序GUID”具有很好的特性,当作为带有聚集索引的数据库主键使用时,始终将新记录追加到数据库表的“末尾”。这有助于防止数据库索引碎片化和页面分裂。

如果使用随机GUID作为带有聚集索引的数据库主键,则从物理分配的角度来看,新记录将随机插入表的“中间”,这会导致索引碎片化和部分填充的数据库页面。

使用顺序GUID仍然可以在多个系统上独立生成GUID,并确信不会发生任何冲突(这是使用顺序整数作为主键而不分配“范围”或对每个系统使用不同的种子和增量所没有的属性,在大型分布式应用程序中,这是一种行政噩梦)。


这个问题是关于在C#中生成GUID的,而这个答案与此无关;-1。 - Mark Amery

0

有关维基百科MSDN的详细信息。

简而言之,GUID是一个随机的128位整数,但我们使用十六进制数字格式化它们,以便它们易于阅读(这就是您看到的8-4-4-4-12格式)。至于它是如何生成的,请参见链接的维基百科文章。


根据您提供的维基百科文章,有5种不同的标准UUID生成算法。因此,“请参见链接的维基百科文章”并不能回答C#特别是如何生成UUID的问题。 - Mark Amery

0
GUID的各个部分如下:
  • 60位时间戳
  • 48位计算机标识符
  • 14位唯一标识符
  • 6位固定值

总共128位。


1
-1;这个答案并不适用于在Windows上使用C#的Guid.NewGuid生成的GUID,如https://dev59.com/CHE85IYBdhLWcg3wejfn#2757969中所述。实际上,所有语言/操作系统/框架都有很好的隐私原因来避免您在此处描述的GUID生成算法(或者至少不要将其作为默认值使用),正如罪犯David L. Smith 可以向您解释的 - Mark Amery

0
我们可以直接对比生成的GUID来进行检查。我更喜欢一些源文件,但说实话,维基百科的概述已经很好了。第一个相关部分是关于GUID变体的。总结一下:为了确定GUID的类型,我们需要查看变体和可能的版本
.NET生成的GUID是哪种变体(和版本)?
变体由第9个八位字节的初始位指示。它通过将初始位固定为010110111来完成。使用
string show(Guid g) => $"{g}: fam {Convert.ToString(g.ToByteArray()[8], 2)}";
show(Guid.NewGuid())

我们可能会得到"1b579ecd-b72c-4550-b10b-f8dd41699ac1: fam 10110001"。该家族被固定在10,OSF DCE UUID或变体2上。这个变体定义了一个版本,保存在第7个八位字节的更重要的“nibble”中。
但是,有一个复杂因素:endianness in encoding。这些基于MS的部分以小端格式存储。让我们来看一下:用另一种快速方法。
void SeeOrder(Guid g) => Console.WriteLine($"{g}\n{g:N}\n{string.Join("", g.ToByteArray().Select(b => $"{b:x2}"))}"); 

我们会看到一个结果像这样
dd3f9bdf-ba47-4411-84c8-6cfcc5d39bb5
dd3f9bdfba47441184c86cfcc5d39bb5
df9b3fdd47ba114484c86cfcc5d39bb5

第一、二、三组被转置,因为每个都是一个完整的整数(分别为时间的两个和一个保留位;分别为32、16和16位)。而最后两组则不是,因为最后8个八位字节是一个“字符串”:1个保留字节(表示“family”)和7个“node”字节。
所以第7个版本字节实际上被编码为第8个(索引为7)。将show扩展以突出显示版本。
string show(Guid g) => $"{g}: fam {Convert.ToString(g.ToByteArray()[8], 2)}, ver {g.ToByteArray()[7].ToString("x")}";

我们将得到"154573de-d420-433b-bb25-aff50590038b: fam 10111011, ver 43"。所讨论的四位二进制数是4,因此版本为4:伪随机生成。
如您在上述示例中所见,变体2版本显示为第三组的第一个十六进制字符。您会注意到,在.NET GUID中,它总是4。阅读完这篇文章后,您将永远不会忽略那个4。
不客气。

-2

Guid是一个唯一的数字,有数十亿个排列组合,由十六进制和数字混合组成。它是基于不同的因素生成的,例如时间、Windows版本、系统资源(如硬盘、主板、设备)等等。 Guid保证是唯一的。(感谢ck!)我从未见过在连续运行中g具有相同值的情况。

Guid g = new Guid.NewGuid();
Guid g = Guid.NewGuid(); /* 感谢Dave! */

在互斥体中更常用,例如创建应用程序的单个实例,并使用带有guid的互斥体可以保证应用程序实例的生命周期。

甚至在某些情况下,它也被用于数据库,但使用方法受到反对。


GUID并不能保证唯一性,只是生成两个相同的概率非常低。 - cjk
如果使用Guid.NewGuid(),则不需要(实际上也不能)使用new关键字。 - Dave Mateer

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