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
?
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:
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.
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.
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).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With