Я хочу попытаться преобразовать строку в 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 недействительны (?)
Обновление 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 Возможно, я разбираю поисковое выражение, введенное пользователем.

Если они вводят 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. Мне не нужно было оправдывать вопрос.
Ты прав; вы должны нет, чтобы оправдать вопрос. Однако я с интересом прочитал обоснование (так как оно очень похоже на то, почему я здесь это читаю). Итак, спасибо за большое оправдание.
@Jeff, вероятно, потому что OP редактировал его более 10 раз - см. мета на вики сообщества
Продолжайте искать на этой странице решения с Guid.TryParse или Guid.TryParseExact. С .NET 4.0 + вышеупомянутое решение не самое элегантное
@dplante Когда я изначально задал вопрос в 2008 году, 4.0 не было. Вот почему вопрос и принятый ответ такие, какие они есть.
Я не знаю ответа на этот вопрос, но, как бы то ни было, вы правы, что не хотите использовать здесь блок try / catch. Требуется много вычислений, чтобы перевесить затраты на перехват исключения, плюс попытка / отлов не подходит для обычного потока программы! Конечно, если вам не нужно перехватывать множество исключений в этом коде, это не так уж важно.





Запустите потенциальный GUID с помощью RegEx или какого-либо настраиваемого кода, который выполняет проверку работоспособности, чтобы убедиться, что строка хотя бы выглядит как GUID и состоит только из допустимых символов (и, возможно, что она соответствует общему формату). Если он не проходит проверку работоспособности, возвращает ошибку - это, вероятно, отсеивает подавляющее большинство недопустимых строк.
Затем преобразуйте строку, как указано выше, по-прежнему обнаруживая исключение для нескольких недопустимых строк, прошедших проверку работоспособности.
Джон Скит провел анализ чего-то похожего для синтаксического анализа Ints (до того, как TryParse был в Framework): Проверка возможности преобразования строки в Int32
Однако, как указывает ЭнтониУДжонс, вам, вероятно, не стоит об этом беспокоиться.
Вам это не понравится, но почему вы думаете, что перехват исключения будет медленнее?
Сколько неудачных попыток разобрать GUID вы ожидаете по сравнению с успешными?
Мой совет - используйте только что созданную функцию и профилируйте свой код. Если вы обнаружите, что эта функция действительно является точкой доступа тогда, исправьте ее, но не раньше.
Хороший ответ, преждевременная оптимизация - это корень всех зол.
Плохо полагаться на исключения, которые не являются исключениями. Это дурная привычка, к которой я бы не хотел, чтобы кто-то вошел в нее. И я особенно не хотел бы делать это в библиотечной рутине, где люди будут верить, что это работает и хорошо.
Анонимно, в вашем исходном вопросе указана производительность как причина, по которой вы хотели избежать исключений. Если это не так, то, возможно, вам стоит подправить свой вопрос.
Теперь я знаю, что чувствует Раймонд Чен.
@Kev "Хороший ответ, преждевременная оптимизация - корень всех зол". Я согласен, Кев, нет необходимости оптимизировать что-то, что в относительном выражении ничего не будет стоить. Это зависит от объема того, что вы делаете, но в этом случае я думаю, что решение PInvoke является излишним, а не попыткой улова.
«Сколько неудачных попыток разобрать GUID вы ожидаете по сравнению с успешными?» Я ожидаю, что каждая строка, которую я анализирую, не будет GUID. Смотрите рутирование папки NSE: msdn.microsoft.com/en-us/library/cc144096(VS.85).aspx
Исключение следует использовать в ИСКЛЮЧИТЕЛЬНЫХ случаях, означающих: не управляется разработчиком. Я противник метода Microsoft «все исключения» для управления ошибками. Правила защитного программирования. Пожалуйста, разработчики фреймворка Microsoft рассмотрите возможность добавления TryParse в класс Guid.
в ответ на мой собственный комментарий => Guid.TryParse был добавлен в framework 4.0 --- msdn.microsoft.com/en-us/library/… --- спасибо MS за такую быструю реакцию;)
Что ж, вот регулярное выражение, которое вам понадобится ...
^[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, чтобы гарантировать этот тип проверки!
Гм, идентификаторы GUID IIRC, которые генерируются из отметки времени, обычно считаются плохой идеей, а другой тип (тип 4) полностью случайен
Насколько я знаю, в mscrolib нет чего-то вроде Guid.TryParse. Согласно справочному источнику, тип Guid имеет мега-сложный конструктор, который проверяет все виды форматов guid и пытается их проанализировать. Не существует вспомогательного метода, который можно было бы вызвать даже через отражение. Я думаю, вам нужно искать сторонние парсеры Guid или писать свои собственные.
bool IsProbablyGuid(string s)
{
int hexchars = 0;
foreach(character c in string s)
{
if (IsValidHexChar(c))
hexchars++;
}
return hexchars==32;
}
"-" "{" "}" ("и") "не являются допустимыми шестнадцатеричными символами, но допустимы в строке guid.
и этот код будет работать отлично, если строка ввода guid содержит эти не шестнадцатеричные символы
Хотя является верно, что использование ошибок обходится дороже, большинство людей считает, что большинство их 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;
}
Я бы хотя бы переписал это так:
try
{
value = new Guid(s);
return true;
}
catch (FormatException)
{
value = Guid.Empty;
return false;
}
Вы не хотите говорить «недопустимый GUID» для SEHException, ThreadAbortException или других фатальных или не связанных с ними вещей.
Обновлять: Начиная с .NET 4.0, для Guid доступен новый набор методов:
На самом деле, их следует использовать (хотя бы потому, что они не «наивно» реализованы с использованием try-catch внутренне).
Тесты производительности
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 в строковое представление в Guid, используйте
new Guid(someString);
Вы запускали их с включенным или выключенным отладчиком? Производительность генерации исключений повышена в несколько раз без подключения отладчика.
Спасибо. Я сам собирался задать этот вопрос. Рад, что нашел твой ответ.
Я создал новый файл с именем PInvoke.cs с фрагментом кода PInvoke пространства имен сверху, но я не могу заставить код работать. Когда я отлаживаю, я вижу, что результат CLSIDFromString ВСЕГДА отрицательный. Я попытался изменить вызывающую строку на: int hresult = PInvoke.ObjBase.CLSIDFromString (Guid.NewGuid (). ToString (), out value); но он все равно всегда отрицательный. Что я делаю не так?
Взаимодействие медленнее, чем просто перехват исключения:
На счастливом пути с 10000 гидами:
Exception: 26ms
Interop: 1,201ms
На несчастном пути:
Exception: 1,150ms
Interop: 1,201ms
Он более последовательный, но при этом постоянно медленнее. Мне кажется, вам лучше настроить отладчик так, чтобы он останавливался только на необработанных исключениях.
"ваш отладчик останавливался только на необработанных исключениях" Не вариант.
@Ian Boyd - Если вы используете какую-либо из редакций VS (включая Express), это вариант является. msdn.microsoft.com/en-us/library/038tzxdw.aspx.
Я имел в виду, что это невыполнимый вариант. Мол, «Неудача - это не вариант». Это является вариант, но я не собираюсь использовать его.
если TypeOf ctype (myvar, Object) является Guid, то .....
for usability reasons - the debugger pops up
Если вы выбираете подход try / catch, вы можете добавить атрибут [System.Diagnostics.DebuggerHidden], чтобы убедиться, что отладчик не сломается, даже если вы установили его прерывание при выбросе.
Ctor Guid - это в значительной степени скомпилированное регулярное выражение, поэтому вы получите точно такое же поведение без накладных расходов на исключение.
Еще более крутым решением было бы динамическое инструментирование метода, заменяя «бросать новый» на лету.
Я пытался украсть код у ctor, но он ссылается на многие внутренние частные классы для выполнения своей поддержки. Поверьте, это была моя первая попытка.
У меня была аналогичная ситуация, и я заметил, что почти никогда не было недопустимой строки длиной 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 принимает в своем ctor не только форму пунктирной строки. Идентификаторы GUID могут иметь окружающие фигурные скобки с тире или не содержать тире или фигурных скобок. Этот код будет генерировать ложноотрицательные результаты при использовании этими альтернативными, но также вполне допустимыми строковыми формами.
Допустимая длина для GUID строковой формы - 32, 36 и 38 - чистый шестнадцатеричный, пунктирный и фигурные скобки с тире соответственно.
@Chris, ваша точка зрения верна, но идея @JBrooks о разумности проверки предполагаемого GUID перед тем, как бросить вызов try / catch, имеет смысл, особенно если подозрительный ввод является обычным. Может быть, что-то вроде if (value == null || value.Length <30 || value.length> 40) {value = Guid.Empty; return false;}
В самом деле, это было бы лучше, хотя я бы держал диапазон более узким, 32..38, а не 30..40.
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
Я голосую за ссылку GuidTryParse, размещенную выше Джон или аналогичное решение (IsProbablyGuid). Я напишу такой же для моей библиотеки преобразования.
Я думаю, что это совершенно неуместно, что этот вопрос настолько сложен. Ключевое слово «is» или «as» будет подходящим, ЕСЛИ Guid может иметь значение null. Но по какой-то причине, даже если SQL Server это устраивает, .NET - нет. Почему? В чем ценность Guid.Empty? Это просто глупая проблема, созданная дизайном .NET, и меня действительно беспокоит, когда соглашения языка действуют сами по себе. На данный момент наиболее эффективным ответом было использование COM-взаимодействия, потому что Framework не справляется с этим изящно? "Может ли эта строка быть GUID?" должен быть вопрос, на который легко ответить.
Можно полагаться на выброшенное исключение, пока приложение не появится в Интернете. В этот момент я просто настроился на атаку отказа в обслуживании. Даже если меня не «атакуют», я знаю, что какой-то yahoo собирается обезьянничать с URL-адресом, или, возможно, мой отдел маркетинга отправит неверную ссылку, и тогда мое приложение должно сильно пострадать в производительности, что МОЖЕТ принести сервер, потому что я не писал свой код для решения проблемы, которая НЕ ДОЛЖНА случиться, но мы все знаем, что БУДЕТ.
Это немного размывает черту в «Исключении», но в итоге, даже если проблема нечасто, если это может случиться достаточно много раз за короткий промежуток времени, что ваше приложение выйдет из строя, обслуживая все уловы из всего этого, тогда я думаю, что выброс исключения - это плохой тон.
TheRage3K
В .NET 4.0 вы можете написать следующее:
public static bool IsValidGuid(string str)
{
Guid guid;
return Guid.TryParse(str, out guid);
}
Это действительно должен быть один из лучших ответов.
С помощью метода расширения в C#
public static bool IsGUID(this string text)
{
return Guid.TryParse(text, out Guid guid);
}
Почему это вики сообщества?