Проверьте, является ли 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 возможно, я анализирую выражение поиска, введенное пользователем.
если они вводят 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-это в значительной степени скомпилированное регулярное выражение, таким образом, вы получите точно такое же поведение без накладных расходов исключения.
- означает ли это обратное проектирование? Я думаю, что да, и как таковой может быть незаконным.
- сломается, если форма GUID изменения.
еще более крутое решение было бы динамически прибором метода, заменив "бросить новое" на лету.
Я голосую за ссылку GuidTryParse, опубликованную выше Йон или аналогичное решение (IsProbablyGuid). Я буду писать такие же для моей библиотеки конверсий.
Я думаю, что это безнадежно, что этот вопрос должен быть настолько сложным. Ключевое слово" is "или" as " было бы просто прекрасно, если Guid может быть нулевым. Но по какой-то причине, хотя SQL Server в порядке с этим, .NET-нет. Почему? Какова ценность Guid.Пусто? Это просто глупая проблема, созданная дизайн .NET, и это действительно меня беспокоит, когда соглашения языка наступают на себя. Наиболее эффективным ответом до сих пор было использование com-взаимодействия, потому что платформа не обрабатывает его изящно? - Эта струна может быть GUID?- на этот вопрос легко ответить.
полагаться на исключение бросается в порядке, пока приложение не выходит в интернет. В этот момент я просто настроил себя на атаку отказа в обслуживании. Даже если меня не "атакуют", я знаю, что некоторые yahoo собираюсь обезьянничать с URL-адресом, или, может быть, мой отдел маркетинга отправит искаженную ссылку, а затем мое приложение должно пострадать от довольно сильного удара по производительности, который может сбить сервер, потому что я не написал свой код, чтобы справиться с проблемой, которая не должна произойти, но мы все знаем, что произойдет.
это размывает строку немного на "исключение" - но итог, даже если проблема нечаста, если это может произойти достаточно раз за короткий промежуток времени, что ваше приложение аварийно завершает работу обслуживание ловит от всего этого, то я думаю, что исключение-это плохая форма.
TheRage3K
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);
}