Проверить, является ли строка guid без исключения? - PullRequest
178 голосов
/ 19 сентября 2008

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

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

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

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

не подходит.

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

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


Обновление 1

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


Обновление 2

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

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

Пример 1 Расширения пространства имен можно указать, добавив GUID к имени папки . Я мог бы анализировать имена папок, проверяя, является ли текст после окончательного . 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 хороших гидов и 10 000 плохих гидов.

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 ]

107 голосов
/ 13 ноября 2008

Результаты тестов

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 Interop.

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

new Guid(someString);
88 голосов
/ 31 декабря 2009

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

66 голосов
/ 19 сентября 2008

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

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

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

39 голосов
/ 30 июня 2011

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

public static bool IsValidGuid(string str)
{
    Guid guid;
    return Guid.TryParse(str, out guid);
}
21 голосов
/ 26 сентября 2008

Я бы хотя бы переписал это как:

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

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

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

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

13 голосов
/ 14 ноября 2008

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

На счастливом пути, с 10 000 гидов:

Exception:    26ms
Interop:   1,201ms

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

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

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

9 голосов
/ 19 сентября 2008

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

^[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, чтобы гарантировать такой тип проверки!

5 голосов
/ 27 мая 2009

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

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

4 голосов
/ 26 июля 2009

У меня была похожая ситуация, и я заметил, что почти никогда не было недопустимой строки длиной 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;
    }
}
4 голосов
/ 19 сентября 2008

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

Вот, пожалуйста,

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;
  }
...