如果您查看Guid(string)
in the .NET 4.5.2 source code的构造函数的源代码,则如下所示:
public Guid(String g)
{
if (g==null) {
throw new ArgumentNullException("g");
}
Contract.EndContractBlock();
this = Guid.Empty;
GuidResult result = new GuidResult();
result.Init(GuidParseThrowStyle.All);
if (TryParseGuid(g, GuidStyles.Any, ref result)) {
this = result.parsedGuid;
}
else {
throw result.GetGuidParseException();
}
}
问题是该行this = Guid.Empty;
的目的是什么?
我可以看到,string g
方法可以成功解析TryParseGuid
,然后会分配this
。如果不能,那么将抛出异常。
假设您写道:
var guid = new Guid("invalidguid");
这会导致异常,我会假设guid的值是未定义的。那么为什么需要将this
分配给Guid.Empty
?
答案 0 :(得分:7)
这更像是一种风格问题 - 在功能上它是多余的,编译器甚至可以在生成的代码中优化分配给Guid.Empty
。
防御性编码建议变量应始终具有明确分配的初始值。为什么?因为它减少了歧义,特别是对于那些不熟悉给定编程语言/平台细节的人。例如,人们可能会问:
陈述:
Guid id = Guid.Empty;
所有这些问题基本上都是通过阅读代码来解答的,而无需借助阅读文档和/或来源。此外,明确的是变量id
开始为空,这向读者表明它将在代码中稍后设置其值。
答案 1 :(得分:5)
我会猜测这是一个历史神器。
在3.5版本中,GUID是就地构造的,所有文本解析都包含在构造函数方法本身中。
我猜想有一次开发人员决定将所有解析代码重构为辅助方法 1 ,此时他们已经遇到了编译错误,因为你必须在调用实例方法之前肯定会分配this
。
GuidResult
辅助器结构似乎已经在其他某个时间点引入,此时这些解析方法可能变为static
并且与GuidResult
相反而不是实际目前正在构建Guid
- 此时所有内容都会再次简化,并且不需要明确的分配。
这就是反射器将3.5版本反编译为:
public Guid(string g)
{
if (g == null)
{
throw new ArgumentNullException("g");
}
int startIndex = 0;
int parsePos = 0;
try
{
int num2;
long num3;
if (g.IndexOf('-', 0) >= 0)
{
string str = g.Trim();
if (str[0] == '{')
{
if ((str.Length != 0x26) || (str[0x25] != '}'))
{
throw new FormatException(Environment.GetResourceString("Format_GuidInvLen"));
}
startIndex = 1;
}
else if (str[0] == '(')
{
if ((str.Length != 0x26) || (str[0x25] != ')'))
{
throw new FormatException(Environment.GetResourceString("Format_GuidInvLen"));
}
startIndex = 1;
}
else if (str.Length != 0x24)
{
throw new FormatException(Environment.GetResourceString("Format_GuidInvLen"));
}
if (((str[8 + startIndex] != '-') || (str[13 + startIndex] != '-')) || ((str[0x12 + startIndex] != '-') || (str[0x17 + startIndex] != '-')))
{
throw new FormatException(Environment.GetResourceString("Format_GuidDashes"));
}
parsePos = startIndex;
this._a = TryParse(str, ref parsePos, 8);
parsePos++;
this._b = (short) TryParse(str, ref parsePos, 4);
parsePos++;
this._c = (short) TryParse(str, ref parsePos, 4);
parsePos++;
num2 = TryParse(str, ref parsePos, 4);
parsePos++;
startIndex = parsePos;
num3 = ParseNumbers.StringToLong(str, 0x10, 0x2000, ref parsePos);
if ((parsePos - startIndex) != 12)
{
throw new FormatException(string.Format(CultureInfo.CurrentCulture, Environment.GetResourceString("Format_GuidInvLen"), new object[0]));
}
this._d = (byte) (num2 >> 8);
this._e = (byte) num2;
num2 = (int) (num3 >> 0x20);
this._f = (byte) (num2 >> 8);
this._g = (byte) num2;
num2 = (int) num3;
this._h = (byte) (num2 >> 0x18);
this._i = (byte) (num2 >> 0x10);
this._j = (byte) (num2 >> 8);
this._k = (byte) num2;
}
else if (g.IndexOf('{', 0) >= 0)
{
int num5 = 0;
int length = 0;
g = EatAllWhitespace(g);
if (g[0] != '{')
{
throw new FormatException(Environment.GetResourceString("Format_GuidBrace"));
}
if (!IsHexPrefix(g, 1))
{
throw new FormatException(string.Format(CultureInfo.CurrentCulture, Environment.GetResourceString("Format_GuidHexPrefix"), new object[] { "{0xdddddddd, etc}" }));
}
num5 = 3;
length = g.IndexOf(',', num5) - num5;
if (length <= 0)
{
throw new FormatException(Environment.GetResourceString("Format_GuidComma"));
}
this._a = ParseNumbers.StringToInt(g.Substring(num5, length), 0x10, 0x1000);
if (!IsHexPrefix(g, (num5 + length) + 1))
{
throw new FormatException(string.Format(CultureInfo.CurrentCulture, Environment.GetResourceString("Format_GuidHexPrefix"), new object[] { "{0xdddddddd, 0xdddd, etc}" }));
}
num5 = (num5 + length) + 3;
length = g.IndexOf(',', num5) - num5;
if (length <= 0)
{
throw new FormatException(Environment.GetResourceString("Format_GuidComma"));
}
this._b = (short) ParseNumbers.StringToInt(g.Substring(num5, length), 0x10, 0x1000);
if (!IsHexPrefix(g, (num5 + length) + 1))
{
throw new FormatException(string.Format(CultureInfo.CurrentCulture, Environment.GetResourceString("Format_GuidHexPrefix"), new object[] { "{0xdddddddd, 0xdddd, 0xdddd, etc}" }));
}
num5 = (num5 + length) + 3;
length = g.IndexOf(',', num5) - num5;
if (length <= 0)
{
throw new FormatException(Environment.GetResourceString("Format_GuidComma"));
}
this._c = (short) ParseNumbers.StringToInt(g.Substring(num5, length), 0x10, 0x1000);
if ((g.Length <= ((num5 + length) + 1)) || (g[(num5 + length) + 1] != '{'))
{
throw new FormatException(Environment.GetResourceString("Format_GuidBrace"));
}
length++;
byte[] buffer = new byte[8];
for (int i = 0; i < 8; i++)
{
if (!IsHexPrefix(g, (num5 + length) + 1))
{
throw new FormatException(string.Format(CultureInfo.CurrentCulture, Environment.GetResourceString("Format_GuidHexPrefix"), new object[] { "{... { ... 0xdd, ...}}" }));
}
num5 = (num5 + length) + 3;
if (i < 7)
{
length = g.IndexOf(',', num5) - num5;
if (length <= 0)
{
throw new FormatException(Environment.GetResourceString("Format_GuidComma"));
}
}
else
{
length = g.IndexOf('}', num5) - num5;
if (length <= 0)
{
throw new FormatException(Environment.GetResourceString("Format_GuidBraceAfterLastNumber"));
}
}
uint num8 = (uint) Convert.ToInt32(g.Substring(num5, length), 0x10);
if (num8 > 0xff)
{
throw new FormatException(Environment.GetResourceString("Overflow_Byte"));
}
buffer[i] = (byte) num8;
}
this._d = buffer[0];
this._e = buffer[1];
this._f = buffer[2];
this._g = buffer[3];
this._h = buffer[4];
this._i = buffer[5];
this._j = buffer[6];
this._k = buffer[7];
if ((((num5 + length) + 1) >= g.Length) || (g[(num5 + length) + 1] != '}'))
{
throw new FormatException(Environment.GetResourceString("Format_GuidEndBrace"));
}
if (((num5 + length) + 1) != (g.Length - 1))
{
throw new FormatException(Environment.GetResourceString("Format_ExtraJunkAtEnd"));
}
}
else
{
string s = g.Trim();
if (s.Length != 0x20)
{
throw new FormatException(Environment.GetResourceString("Format_GuidInvLen"));
}
for (int j = 0; j < s.Length; j++)
{
char c = s[j];
if ((c < '0') || (c > '9'))
{
char ch2 = char.ToUpper(c, CultureInfo.InvariantCulture);
if ((ch2 < 'A') || (ch2 > 'F'))
{
throw new FormatException(Environment.GetResourceString("Format_GuidInvalidChar"));
}
}
}
this._a = ParseNumbers.StringToInt(s.Substring(startIndex, 8), 0x10, 0x1000);
startIndex += 8;
this._b = (short) ParseNumbers.StringToInt(s.Substring(startIndex, 4), 0x10, 0x1000);
startIndex += 4;
this._c = (short) ParseNumbers.StringToInt(s.Substring(startIndex, 4), 0x10, 0x1000);
startIndex += 4;
num2 = (short) ParseNumbers.StringToInt(s.Substring(startIndex, 4), 0x10, 0x1000);
startIndex += 4;
parsePos = startIndex;
num3 = ParseNumbers.StringToLong(s, 0x10, startIndex, ref parsePos);
if ((parsePos - startIndex) != 12)
{
throw new FormatException(string.Format(CultureInfo.CurrentCulture, Environment.GetResourceString("Format_GuidInvLen"), new object[0]));
}
this._d = (byte) (num2 >> 8);
this._e = (byte) num2;
num2 = (int) (num3 >> 0x20);
this._f = (byte) (num2 >> 8);
this._g = (byte) num2;
num2 = (int) num3;
this._h = (byte) (num2 >> 0x18);
this._i = (byte) (num2 >> 0x10);
this._j = (byte) (num2 >> 8);
this._k = (byte) num2;
}
}
catch (IndexOutOfRangeException)
{
throw new FormatException(Environment.GetResourceString("Format_GuidUnrecognized"));
}
}
因此很明显在3.5和4.5.2之间引入了辅助方法。从那里开始,假设首先引入辅助方法(作为实例方法,因此需要明确赋值),然后引入辅助结构(GuidResult
)。
如果您将所有4.5.2代码复制到一个新项目中,请删除您询问和编译的行,一切都还可以。
1 可能支持在Parse
上引入似乎已出现在.NET 4.0时间范围内的TryParse
/ Guid
方法。
答案 2 :(得分:1)
从本质上讲,你在一定程度上回答了自己的问题。
如果抛出错误并且此行= Gui.Empty不存在,则可能导致为变量分配未定义的值。因此,即使您有异常,变量本身也是未定义的。
可能存在这样的用例。例如,如果变量是全局变量,或者即使gui生成失败或者至少需要检查值,也需要使用变量....因此,即使抛出异常,最好有一个定义的值(在这种情况下为0),如果有人需要一个值,即使构造函数由于某种原因失败,也会导致不稳定的行为。
这就是这种结构存在的主要原因(至少我希望微软有这样的理由,除了我不能想到这样做的逻辑原因)。
答案 3 :(得分:0)
我想我对这一行有一个令人满意的解释。
在struct参数化构造函数中,你必须分配所有字段,否则你得到类似这样的东西“错误1字段'My.Guid._j'必须完全分配,然后才能将控制权返回给调用者C:\ Work \ Projects \ Guid \ Guid \ Program.cs 153 16 Guid“
AFAIU,你必须这样做的原因是当你调用一个参数化的结构构造函数时,你覆盖默认的CLR结构初始化行为,它只是将分配的内存归零,而不是初始化每个字段。 (虽然我可能错了)
通常,您可以从任何参数化构造函数中调用带有:this()
的无参数构造函数来初始化所有成员,这也适用于此处。但是这会增加额外的调用和额外的内存分配,因此对于性能优化,他们将对默认构造函数的调用替换为静态只读字段Guid.Empty
。
答案 4 :(得分:-1)
Guid.Empty将分配全部为零。如果您不想分配全零,可以使用Guid.NewGuid()方法。这里默认是分配的。