Prueba si string es un guid sin tirar excepciones?

Quiero intentar convertir una cadena en Guid, pero no quiero confiar en las excepciones de captura (

  • por razones de rendimiento: las excepciones son caras
  • por razones de usabilidad: aparece el depurador
  • por razones de diseño: lo esperado no es excepcional

En otras palabras, el código:

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

no es adecuado.

Yo trataría de usar RegEx, pero como el guid puede ser paréntesis envuelto, bloqueado, ninguno envuelto, lo hace difícil.

Además, pensé que ciertos valores de Guid no son válidos (?)


Actualización 1

ChristianK tuvo una buena idea para atrapar solo FormatException , en lugar de todos. Cambió la muestra del código de la pregunta para incluir sugerencia.


Actualización 2

¿Por qué preocuparse por las excepciones arrojadas? ¿De verdad estoy esperando GUID inválidos con tanta frecuencia?

La respuesta es . Es por eso que estoy usando TryStrToGuid – Estoy esperando datos malos.

Ejemplo 1 Las extensiones del espacio de nombres se pueden especificar al agregar un GUID a un nombre de carpeta . Podría estar analizando los nombres de las carpetas, verificando si el texto después de la final . es un 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 

El ejemplo 2 : podría estar ejecutando un servidor web muy utilizado. Quiero verificar la validez de algunos datos publicados. No quiero datos inválidos que atan recursos de 2 a 3 órdenes de magnitud más de lo necesario.

Ejemplo 3 Podría estar analizando una expresión de búsqueda ingresada por un usuario.

enter image description here

Si ingresan GUID, quiero procesarlos especialmente (como buscar específicamente ese objeto, o resaltar y formatear ese término de búsqueda específico en el texto de respuesta).


Actualización 3 – Puntos de referencia de rendimiento

Prueba la conversión de 10.000 buenos Guids y 10.000 malos Guids.

 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 

ps. No debería tener que justificar una pregunta.

Puntos de referencia de rendimiento

 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 (más rápido) Respuesta:

 ///  /// Attempts to convert a string to a guid. ///  /// The string to try to convert /// Upon return will contain the Guid /// Returns true if successful, otherwise false 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 { ///  /// This function converts a string generated by the StringFromCLSID function back into the original class identifier. ///  /// String that represents the class identifier /// On return will contain the class identifier ///  /// Positive or zero if class identifier was obtained successfully /// Negative if the call failed ///  [DllImport("ole32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, PreserveSig = true)] public static extern int CLSIDFromString(string sz, out Guid clsid); } } 

En pocas palabras: si necesita comprobar si una cadena es un guid, y si le importa el rendimiento, use COM Interop.

Si necesita convertir un guid en representación de cadena a un Guid, use

 new Guid(someString); 

Una vez que .net 4.0 está disponible, puede usar Guid.TryParse() .

No te va a gustar esto, pero ¿qué te hace pensar que atrapar la excepción va a ser más lento?

¿Cuántos bashs fallidos para analizar un GUID espera en comparación con los exitosos?

Mi consejo es usar la función que acabas de crear y perfilar tu código. Si encuentra que esta función es realmente un punto de acceso , corríjalo pero no antes.

En .NET 4.0 puede escribir de la siguiente manera:

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

Al menos lo reescribiría como:

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

No desea decir “GUID no válido” en SEHException, ThreadAbortException u otras cosas fatales o no relacionadas.

Actualización : comenzando con .NET 4.0, hay un nuevo conjunto de métodos disponibles para Guid:

  • Guid.TryParse
  • Guid.TryParseExact

En realidad, esos deberían usarse (aunque solo sea por el hecho de que no se implementan “ingenuamente” utilizando try-catch internamente).

Interop es más lento que simplemente atrapar la excepción:

En el camino feliz, con 10,000 Guids:

 Exception: 26ms Interop: 1,201ms 

En el camino infeliz:

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

Es más consistente, pero también es consistentemente más lento. Me parece que sería mejor que configures tu depurador para romper solo con las excepciones no controladas.

Bueno, aquí está la expresión regular que necesitarás …

 ^[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}(}})$ 

Pero eso es solo para empezar. También deberá verificar que las diversas partes, como la fecha / hora, se encuentren dentro de los rangos aceptables. No puedo imaginar que esto sea más rápido que el método de prueba / captura que ya has delineado. ¡Espero que no estés recibiendo tantos GUID inválidos para garantizar este tipo de control!

por razones de usabilidad: aparece el depurador

Si opta por el enfoque try / catch, puede agregar el atributo [System.Diagnostics.DebuggerHidden] para asegurarse de que el depurador no se rompe incluso si lo ha configurado para romper el lanzamiento.

Si bien es cierto que el uso de errores es más costoso, la mayoría de la gente cree que la mayoría de sus GUIDs serán generados por computadora, por lo que un TRY-CATCH no es demasiado costoso, ya que solo genera costos en el CATCH . Puede probarse esto con una simple prueba de los dos (usuario público, sin contraseña).

Aqui tienes:

 using System.Text.RegularExpressions; ///  /// Validate that a string is a valid GUID ///  ///  ///  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; } 

Tuve una situación similar y noté que casi nunca era la cadena inválida de 36 caracteres de largo. Basándome en este hecho, cambié tu código un poco para obtener un mejor rendimiento sin dejar de ser simple.

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

Hasta donde yo sé, no hay nada como Guid.TryParse en mscrolib. De acuerdo con la Fuente de referencia, el tipo Guid tiene un constructor megacomplejo que verifica todo tipo de formatos guid e intenta analizarlos. No hay un método auxiliar que puedas llamar, incluso a través de la reflexión. Creo que tienes que buscar analizadores Guid de terceros o escribir el tuyo propio.

Ejecute el GUID potencial a través de un RegEx o algún código personalizado que realice una comprobación de cordura para asegurarse de que el strig al menos se parece a un GUID y solo consta de caracteres válidos (y tal vez que parece ajustarse al formato general). Si no pasa el control de cordura, devuelve un error; probablemente eliminará la gran mayoría de las cadenas inválidas.

A continuación, convierta la cadena como lo ha hecho anteriormente, sin dejar de detectar la excepción de las pocas cadenas inválidas que pasan por el control de cordura.

Jon Skeet hizo un análisis de algo similar para analizar Ints (antes de que TryParse estuviera en el Framework): Verificando si una cadena se puede convertir a Int32

Sin embargo, como AnthonyWJones indicó, probablemente no deberías preocuparte por esto.

  bool IsProbablyGuid(string s) { int hexchars = 0; foreach(character c in string s) { if(IsValidHexChar(c)) hexchars++; } return hexchars==32; } 
  • Obtener reflector
  • copy’n’paste Guid’s .ctor (String)
  • reemplace cada aparición de “lanzar nuevo …” con “devolver falso”.

El ctor de Guid es prácticamente una expresión regular comstackda, de esa manera obtendrás exactamente el mismo comportamiento sin sobrecargar la excepción.

  1. ¿Constituye esto una ingeniería inversa? Creo que sí, y como tal podría ser ilegal.
  2. Se romperá si el formulario GUID cambia.

Incluso una solución más fresca sería instrumentar dinámicamente un método, reemplazando “lanzar nuevo” sobre la marcha.

Voto el enlace GuidTryParse publicado anteriormente por Jon o una solución similar (IsProbablyGuid). Escribiré uno como esos para mi biblioteca de Conversión.

Creo que es totalmente cojo que esta pregunta tenga que ser tan complicada. La palabra clave “is” o “as” estaría bien SI un Guid pudiera ser nulo. Pero por alguna razón, aunque SQL Server está de acuerdo con eso, .NET no lo está. ¿Por qué? ¿Cuál es el valor de Guid.Empty? Este es solo un problema tonto creado por el diseño de .NET, y realmente me molesta cuando las convenciones de un idioma se pisan a sí mismas. La respuesta de mejor rendimiento hasta ahora ha sido utilizar COM Interop porque el Framework no lo maneja con elegancia. “¿Puede esta cadena ser un GUID?” debería ser una pregunta fácil de responder.

Confiar en la excepción lanzada está bien, hasta que la aplicación se conecte a Internet. En ese momento, me preparé para un ataque de denegación de servicio. Incluso si no me “atacan”, sé que algunos yahoo van a cumplir con la URL, o tal vez mi departamento de marketing enviará un enlace mal formado, y luego mi aplicación tendrá que sufrir un golpe de rendimiento bastante fuerte que PODRÍA traer en el servidor porque no escribí mi código para manejar un problema que NO DEBE pasar, pero todos sabemos que SUCEDERÁ.

Esto borra un poco la línea en “Excepción” – pero la línea de fondo, incluso si el problema no es frecuente, si puede suceder las veces suficientes en un período de tiempo corto que su aplicación se bloquea reparando las capturas de todo, entonces creo que lanzar una excepción es mala forma.

TheRage3K

si TypeOf ctype (myvar, Object) es Guid entonces …..

 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 

Con un método de extensión en C #

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