Проверьте, является ли строка директором, не исключая исключения?

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

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

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

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 - я am, ожидая плохих данных.

Пример 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. Я не должен был бы обосновывать вопрос.

Ответ 1

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

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

Нижняя строка: если вам нужно проверить, является ли строка ориентиром, и вы заботитесь о производительности, используйте COM Interop.

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

new Guid(someString);

Ответ 2

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

Ответ 3

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

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

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

Ответ 4

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

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

Ответ 5

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

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

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

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

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

Ответ 6

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

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

Exception:    26ms
Interop:   1,201ms

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

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

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

Ответ 7

Ну, вот вам регулярное выражение...

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

Ответ 8

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

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

Ответ 9

Хотя верно, что использование ошибок является более дорогостоящим, большинство людей считают, что большинство их 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;
  }

Ответ 10

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

Ответ 11

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

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

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

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

Ответ 12

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

Ответ 13

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

Ответ 14

  • Получить рефлектор
  • copy'n'paste Guid.ctor(String)
  • замените каждое появление "throw new..." на "return false".

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

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

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

Ответ 15

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

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

Опираясь на исключение, которое вы выбрали, все в порядке, пока приложение не появится в Интернете. В этот момент я просто настроился на атаку отказа в обслуживании. Даже если меня не "атакуют", я знаю, что какая-то yahoo собирается обезьяной с URL-адресом, или, может быть, мой отдел маркетинга отправит неверную ссылку, а затем мое приложение должно пострадать от довольно высокой производительности, которую МОЖЕТ принести вниз, потому что я не писал свой код, чтобы справиться с проблемой, которая НЕ ДОЛЖНА произойти, но все мы знаем, СЛУШАЛИ.

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

TheRage3K

Ответ 16

если TypeOf ctype (myvar, Object) Is Guid тогда.....

Ответ 17

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

Ответ 18

С помощью метода расширения в С#

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