Проверьте, является ли string guid без исключения?

Я хочу попытаться преобразовать строку в Guid, но я не хочу полагаться на улавливание исключений (

  • по соображениям производительности-исключения дороги
  • по причинам удобства использования-отладчик всплывает
  • по причинам дизайна-ожидаемое не является исключительным

другими словами код:

public static Boolean TryStrToGuid(String s, out Guid value)
{
    try
    {
        value = new Guid(s);
        return true;
    }
    catch (FormatException)
    {
        value = Guid.Empty;
        return false;
    }
}

не подходит.

Я бы попытался использовать регулярное выражение, но поскольку guid может быть заключен в скобки, Брейс завернут, не завернут, делает его жестким.

кроме того, я думал, что некоторые значения Guid недопустимы(?)


обновление 1

ChristianK была хорошая идея поймать только FormatException, а не все. Изменен пример кода вопроса, чтобы включить предложение.


обновление 2

зачем беспокоиться о исключений? Неужели я так часто жду инвалидных GUID?

ответ да. Вот почему я использую TryStrToGuid-I am ожидают плохих данных.

Пример 1 расширения пространства имен можно указать, добавив идентификатор GUID к имени папки. Возможно, я разбираю имена папок, проверяя, есть ли текст после final . является GUID.

c:Program Files
c:Program Files.old
c:Users
c:Users.old
c:UserManager.{CE7F5AA5-6832-43FE-BAE1-80D14CD8F666}
c:Windows
c:Windows.old

Пример 2 возможно, я запускаю сильно используемый веб-сервер, который хочет проверить достоверность некоторых разнесенных данных. Я не хочу, чтобы недопустимые данные связывали ресурсы на 2-3 порядка выше, чем это необходимо.

Пример 3 возможно, я анализирую выражение поиска, введенное пользователем.

enter image description here

если они вводят GUID, я хочу обработать их специально (например, специально искать этот объект или выделить и отформатировать этот конкретный поисковый термин в ответе текст.)


обновление 3-контрольные показатели производительности

тест преобразования 10 000 хороших GUID и 10 000 плохих GUID.

Catch FormatException:
   10,000 good:     63,668 ticks
   10,000 bad:   6,435,609 ticks

Regex Pre-Screen with try-catch:
   10,000 good:    637,633 ticks
   10,000 bad:     717,894 ticks

COM Interop CLSIDFromString
   10,000 good:    126,120 ticks
   10,000 bad:      23,134 ticks

p.s. Мне не нужно оправдывать вопрос.

18 ответов


Тесты Производительности

Catch exception:
   10,000 good:    63,668 ticks
   10,000 bad:  6,435,609 ticks

Regex Pre-Screen:
   10,000 good:   637,633 ticks
   10,000 bad:    717,894 ticks

COM Interop CLSIDFromString
   10,000 good:   126,120 ticks
   10,000 bad:     23,134 ticks

COM Intertop (самый быстрый) ответ:

/// <summary>
/// Attempts to convert a string to a guid.
/// </summary>
/// <param name="s">The string to try to convert</param>
/// <param name="value">Upon return will contain the Guid</param>
/// <returns>Returns true if successful, otherwise false</returns>
public static Boolean TryStrToGuid(String s, out Guid value)
{
   //ClsidFromString returns the empty guid for null strings   
   if ((s == null) || (s == ""))   
   {      
      value = Guid.Empty;      
      return false;   
   }

   int hresult = PInvoke.ObjBase.CLSIDFromString(s, out value);
   if (hresult >= 0)
   {
      return true;
   }
   else
   {
      value = Guid.Empty;
      return false;
   }
}


namespace PInvoke
{
    class ObjBase
    {
        /// <summary>
        /// This function converts a string generated by the StringFromCLSID function back into the original class identifier.
        /// </summary>
        /// <param name="sz">String that represents the class identifier</param>
        /// <param name="clsid">On return will contain the class identifier</param>
        /// <returns>
        /// Positive or zero if class identifier was obtained successfully
        /// Negative if the call failed
        /// </returns>
        [DllImport("ole32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, PreserveSig = true)]
        public static extern int CLSIDFromString(string sz, out Guid clsid);
    }
}

итог: Если вам нужно проверить, является ли строка guid, и вы заботитесь о производительности, используйте com-взаимодействие.

Если вам нужно преобразовать guid в строковое представление в Guid, используйте

new Guid(someString);

Как только .net 4.0 доступен, вы можете использовать Guid.TryParse().


вам это не понравится, но что заставляет вас думать, что поймать исключение будет медленнее?

сколько неудачных попыток проанализировать GUID вы ожидаете по сравнению с успешными?

мой совет-используйте только что созданную функцию и профилируйте свой код. Если вы обнаружите, что эта функция действительно является точка затем исправить это, но не раньше.


в .NET 4.0 вы можете написать следующее:

public static bool IsValidGuid(string str)
{
    Guid guid;
    return Guid.TryParse(str, out guid);
}

Я бы, по крайней мере, переписал его как:

try
{
  value = new Guid(s);
  return true;
}
catch (FormatException)
{
  value = Guid.Empty;
  return false;
}

вы не хотите говорить "недопустимый идентификатор GUID" в SEHException, ThreadAbortException или других фатальных или не связанных с ними вещах.

обновление: начиная с .NET 4.0, для Guid доступен новый набор методов:

действительно, они должны использоваться (хотя бы для того, что они не являются "наивно" реализовано с использованием try-catch внутренне).


взаимодействие медленнее, чем просто поймать исключение:

в счастливом пути, с 10 000 Guids:

Exception:    26ms
Interop:   1,201ms

на несчастливом пути:

Exception: 1,150ms
  Interop: 1,201ms

это более последовательно, но это также последовательно медленнее. Мне кажется, вам лучше настроить отладчик только на разрыв необработанных исключений.


Ну, вот регулярное выражение, которое вам понадобится...

^[A-Fa-f0-9]{32}$|^({|\()?[A-Fa-f0-9]{8}-([A-Fa-f0-9]{4}-){3}[A-Fa-f0-9]{12}(}|\))?$|^({)?[0xA-Fa-f0-9]{3,10}(, {0,1}[0xA-Fa-f0-9]{3,6}){2}, {0,1}({)([0xA-Fa-f0-9]{3,4}, {0,1}){7}[0xA-Fa-f0-9]{3,4}(}})$

но это только для начала. Вам также нужно будет убедиться, что различные части, такие как дата/время, находятся в допустимых диапазонах. Я не могу представить, что это будет быстрее, чем метод try/catch, который вы уже описали. Надеюсь, вы не получаете столько недействительных GUID, чтобы гарантировать этот тип проверки!


по соображениям удобства использования-отладчик всплывает

Если вы собираетесь использовать подход try/catch, вы можете добавить [System.Диагностика.Атрибут DebuggerHidden], чтобы убедиться, что отладчик не ломается, даже если вы установили его на разрыв при броске.


а то is правда, что использование ошибок дороже, большинство людей считают, что большинство их GUID будут генерироваться компьютером так TRY-CATCH не слишком дорого, так как он генерирует только стоимость на CATCH. Вы можете доказать это себе с помощью простого теста два (публичное пользователя, без пароля).

здесь вы идете:

using System.Text.RegularExpressions;


 /// <summary>
  /// Validate that a string is a valid GUID
  /// </summary>
  /// <param name="GUIDCheck"></param>
  /// <returns></returns>
  private bool IsValidGUID(string GUIDCheck)
  {
   if (!string.IsNullOrEmpty(GUIDCheck))
   {
    return new Regex(@"^(\{{0,1}([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}\}{0,1})$").IsMatch(GUIDCheck);
   }
   return false;
  }

У меня была аналогичная ситуация, и я заметил, что почти никогда не было недопустимой строки длиной 36 символов. Поэтому, основываясь на этом факте, я немного изменил ваш код, чтобы получить лучшую производительность, сохраняя его простым.

public static Boolean TryStrToGuid(String s, out Guid value)
{

     // this is before the overhead of setting up the try/catch block.
     if(value == null || value.Length != 36)
     {  
        value = Guid.Empty;
        return false;
     }

    try
    {
        value = new Guid(s);
        return true;
    }
    catch (FormatException)
    {
        value = Guid.Empty;
        return false;
    }
}

насколько я знаю, Нет ничего похожего на Guid.TryParse в mscrolib. Согласно справочному источнику, тип Guid имеет мега-сложный конструктор, который проверяет все виды форматов guid и пытается их проанализировать. Нет никакого вспомогательного метода, который вы можете вызвать, даже через отражение. Я думаю,вам нужно искать сторонние Парсеры Guid или писать свои собственные.


запустите потенциальный идентификатор GUID, хотя регулярное выражение или какой-то пользовательский код, который проверяет здравомыслие, чтобы убедиться, что strig по крайней мере выглядит как GUID и состоит только из допустимых символов (и, возможно, что он подходит для общего формата). Если он не пройдет проверку здравомыслия, верните ошибку - это, вероятно, отсеет подавляющее большинство недействительных строк.

затем преобразуйте строку, как указано выше, все еще ловя исключение для нескольких недопустимых строк, которые проходят через здравомыслие проверять.

Джон Скит сделал анализ для чего-то подобного для разбора Ints (до того, как TryParse был в рамках): проверка, можно ли преобразовать строку в Int32

, а AnthonyWJones указал, что вы, вероятно, не должны беспокоиться об этом.

 bool IsProbablyGuid(string s)
    {
        int hexchars = 0;
        foreach(character c in string s)
        {
           if(IsValidHexChar(c)) 
               hexchars++;          
        }
        return hexchars==32;
    }

  • Сделать Отражатель
  • копировать-Н-'paste идентификатор GUID .конструктор(строка)
  • замените каждое появление " throw new ..."с"return false".

ctor Guid-это в значительной степени скомпилированное регулярное выражение, таким образом, вы получите точно такое же поведение без накладных расходов исключения.

  1. означает ли это обратное проектирование? Я думаю, что да, и как таковой может быть незаконным.
  2. сломается, если форма GUID изменения.

еще более крутое решение было бы динамически прибором метода, заменив "бросить новое" на лету.


Я голосую за ссылку GuidTryParse, опубликованную выше Йон или аналогичное решение (IsProbablyGuid). Я буду писать такие же для моей библиотеки конверсий.

Я думаю, что это безнадежно, что этот вопрос должен быть настолько сложным. Ключевое слово" is "или" as " было бы просто прекрасно, если Guid может быть нулевым. Но по какой-то причине, хотя SQL Server в порядке с этим, .NET-нет. Почему? Какова ценность Guid.Пусто? Это просто глупая проблема, созданная дизайн .NET, и это действительно меня беспокоит, когда соглашения языка наступают на себя. Наиболее эффективным ответом до сих пор было использование com-взаимодействия, потому что платформа не обрабатывает его изящно? - Эта струна может быть GUID?- на этот вопрос легко ответить.

полагаться на исключение бросается в порядке, пока приложение не выходит в интернет. В этот момент я просто настроил себя на атаку отказа в обслуживании. Даже если меня не "атакуют", я знаю, что некоторые yahoo собираюсь обезьянничать с URL-адресом, или, может быть, мой отдел маркетинга отправит искаженную ссылку, а затем мое приложение должно пострадать от довольно сильного удара по производительности, который может сбить сервер, потому что я не написал свой код, чтобы справиться с проблемой, которая не должна произойти, но мы все знаем, что произойдет.

это размывает строку немного на "исключение" - но итог, даже если проблема нечаста, если это может произойти достаточно раз за короткий промежуток времени, что ваше приложение аварийно завершает работу обслуживание ловит от всего этого, то я думаю, что исключение-это плохая форма.

TheRage3K


Если TypeOf ctype (myvar,Object) является идентификатором Guid .....


Private Function IsGuidWithOptionalBraces(ByRef strValue As String) As Boolean
    If String.IsNullOrEmpty(strValue) Then
        Return False
    End If

    Return System.Text.RegularExpressions.Regex.IsMatch(strValue, "^[\{]?[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}[\}]?$", System.Text.RegularExpressions.RegexOptions.IgnoreCase)
End Function


Private Function IsGuidWithoutBraces(ByRef strValue As String) As Boolean
    If String.IsNullOrEmpty(strValue) Then
        Return False
    End If

    Return System.Text.RegularExpressions.Regex.IsMatch(strValue, "^[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}$", System.Text.RegularExpressions.RegexOptions.IgnoreCase)
End Function


Private Function IsGuidWithBraces(ByRef strValue As String) As Boolean
    If String.IsNullOrEmpty(strValue) Then
        Return False
    End If

    Return System.Text.RegularExpressions.Regex.IsMatch(strValue, "^\{[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}\}$", System.Text.RegularExpressions.RegexOptions.IgnoreCase)
End Function

С методом расширения в C#

public static bool IsGUID(this string text)
{
    return Guid.TryParse(text, out Guid guid);
}