为什么Guid构造函数的源代码包含行" this = Guid.Empty"?

时间:2015-04-08 05:02:15

标签: c# .net

如果您查看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

5 个答案:

答案 0 :(得分:7)

这更像是一种风格问题 - 在功能上它是多余的,编译器甚至可以在生成的代码中优化分配给Guid.Empty

防御性编码建议变量应始终具有明确分配的初始值。为什么?因为它减少了歧义,特别是对于那些不熟悉给定编程语言/平台细节的人。例如,人们可能会问:

  • Guid的默认值是多少?
  • 它应该是一个新的Guid吗?
  • 它应该全是零吗?
  • 它应该是其他一些不确定的状态吗?

陈述:

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()方法。这里默认是分配的。