Подтвердить что ты не робот

Почему исходный код конструктора Guid содержит строку "this = Guid.Empty"?

Если вы посмотрите исходный код конструктора Guid(string) в исходном коде .NET 4.5.2, то он выглядит следующим образом:

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 будет undefined, я бы предположил. Итак, почему необходимо назначить this - Guid.Empty?

4b9b3361

Ответ 1

Это скорее вопрос стиля, чем что-либо еще - функционально он лишний, и компилятор может даже оптимизировать назначение Guid.Empty в сгенерированном коде.

Оборонительное кодирование рекомендует, чтобы переменная всегда имела явно назначенное начальное значение. Зачем? Потому что это уменьшает двусмысленность, особенно для тех, кто не знаком с деталями данного языка/платформы программирования. Например, можно спросить:

  • Каково значение по умолчанию для Guid?
  • Должен ли он быть новым Гидом?
  • Должно ли быть все ноль?
  • Должно ли это какое-то другое неопределенное состояние?

В заявлении:

Guid id = Guid.Empty;

На все эти вопросы в основном отвечают, читая код и не прибегая к чтению документации и/или источника. Кроме того, он явно указывает, что переменная id начинается пустым, что указывает читателю, что оно будет иметь значение, установленное позже в коде.

Ответ 2

Я бы предположил, что это исторический артефакт.

В версии 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/TryParse на Guid, которые, похоже, появились в .NET 4.0.

Ответ 3

В сущности, вы ответили на свой вопрос до некоторой степени.

Если возникает ошибка, и строка this = Gui.Empty отсутствует, это может привести к присвоению значения undefined значению переменной. Таким образом, хотя у вас есть исключение, сама переменная undefined.

Здесь могут быть случаи, когда это плохой случай. Например, если переменная является глобальной, или той, которая должна использоваться, даже если генерация gui потерпела неудачу или ей необходимо хотя бы проверить значение,..... Таким образом, несмотря на то, что выбрано исключение, лучше всего иметь определенное значение (в данном случае 0s), чем значение undefined, что может привести к неустойчивому поведению, если кто-то НУЖДАЕТСЯ, даже если конструктор не работает по какой-либо причине.

И это основная причина существования такой конструкции (по крайней мере, я надеюсь, что у Microsoft была так же справедливо, что я не могу думать о логической причине для этого).

Ответ 4

Я думаю, что у меня есть удовлетворительное объяснение этой линии.

В конструкторе с параметризацией структуры вам нужно назначить все поля, иначе вы получите что-то вроде этого: "Поле Error 1" My.Guid._j должно быть полностью назначено до того, как элемент управления будет возвращен вызывающему пользователю C:\Work\Projects\Guid\Guid\Program.cs 153 16 Руководство "

AFAIU, причина, по которой вы должны это сделать, заключается в том, что при вызове параметризованного конструктора struct вы переопределяете поведение инициализации структуры CLR по умолчанию, которое просто обнуляет выделенную память вместо инициализации каждого поля. (Возможно, я ошибаюсь)

Обычно вы вызываете конструктор без параметров с :this() из любого параметризованного конструктора для инициализации всех членов, и это тоже будет работать. Но это добавляет дополнительный вызов и дополнительные распределения памяти, поэтому для оптимизации производительности они заменили вызов конструктора по умолчанию с назначением статическому полю readonly Guid.Empty.

Ответ 5

Guid.Empty назначит все нули. Если вы не хотите назначать все нули, вы можете использовать метод Guid.NewGuid(). Здесь присваивается значение по умолчанию.