Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does the source code for the Guid constructor contain the line "this = Guid.Empty"?

Tags:

c#

.net

If you look a the source code for the constructor of Guid(string) in the .NET 4.5.2 source code it is as follows:

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();
    }
}

The question is what is the purpose of the line this = Guid.Empty;?

From what I can see if string g can successfully be parsed in the TryParseGuid method then this will be assigned. If it can't then an exception will be thrown.

Suppose you wrote:

var guid = new Guid("invalidguid");

This would cause an exception and the value of guid would be undefined I would assume. So why the need to assign this to Guid.Empty?

like image 579
neodymium Avatar asked Apr 08 '15 05:04

neodymium


3 Answers

This is more a matter of style than anything else - functionally it's superfluous and the compiler may even optimize away the assignment to Guid.Empty in the generated code.

Defensive coding recommends that variable should always have an initial value explicitly assigned. Why? Because it reduces ambiguity, especially for those unfamiliar with the details of a given programming language/platform. For example one might ask:

  • What is the default value of a Guid?
  • Should it be a new Guid?
  • Should it be all zero's?
  • Should it be some other indeterminate state?

In stating:

Guid id = Guid.Empty;

All these questions are essentially answered by reading the code and without having to resort to reading the documentation and/or source. Further, it's explicit that the variable id starts out being empty which indicates to the reader that it'll have its value set later in the code.

like image 177
Rich Turner Avatar answered Nov 08 '22 02:11

Rich Turner


I would guess that this is a historical artifact.

In the 3.5 version, the GUID is constructed in-place, with all of the text parsing all included within the constructor method itself.

I would guess that at one point the developers decided to refactor all of the parsing code out into helper methods1, at which point they'd have hit a compiler error because you have to definitely assign this before calling instance methods.

The GuidResult helper structure seems to have been introduced at some other point in time, at which point these parsing methods could then become static and work against the GuidResult rather than the actual Guid currently being constructed - at which point everything becomes simplified again and that definite assignment isn't required.


This is what reflector decompiles the 3.5 version to:

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"));
    }
}

So obviously between 3.5 and 4.5.2, the helper methods have been introduced. From there, it's supposition that the helper methods were introduced first (as instance methods, and so requiring definite assignment) and then the helper struct (GuidResult) was introduced afterwards.

If you copy all of the 4.5.2 code into a new project, remove the line you ask about and compile, everything is still fine.


1Probably to support the introduction of Parse/TryParse methods on Guid which seem to have appeared in the .NET 4.0 time frame.

like image 5
Damien_The_Unbeliever Avatar answered Nov 08 '22 03:11

Damien_The_Unbeliever


In essence you answered your own question to an extent.

If an error is thrown and the line this = Gui.Empty is not there it could result in an undefined value beign assigned to the variable. Thus even though you have an exception the variable itself is undefined.

There can be use cases where just that is a bad case. For example if the variable is a global one, or one that needs to be used even if the gui generation failed or it needs to at least be checked for a value, .... . Thus even though an exception is thrown it is better to have a defined value (in this case 0s) than an undefined value at all which could lead to erratic behaviour if someone NEEDS a value even if the constructor fails for some reason.

And that is the main reason for such a construct to exist (at least I hope that microsoft had THAT reason as honestly aside from that I can't think of a logical reason for doing that).

like image 1
Thomas Avatar answered Nov 08 '22 04:11

Thomas