que - string guid c#
Prueba si string es un guid sin tirar excepciones? (18)
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.
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 sí . 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.
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.
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:
En realidad, esos deberían usarse (aunque solo sea por el hecho de que no se implementan "ingenuamente" utilizando try-catch internamente).
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!
Con un método de extensión en C #
public static bool IsGUID(this string text)
{
return Guid.TryParse(text, out Guid guid);
}
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.
En .NET 4.0 puede escribir de la siguiente manera:
public static bool IsValidGuid(string str)
{
Guid guid;
return Guid.TryParse(str, out guid);
}
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.
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.
No te va a gustar esto, pero ¿qué te hace pensar que atrapar la excepción va a ser más lento?
¿Cuántos intentos 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.
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 two (usuario público, sin contraseña).
Aqui tienes:
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;
}
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;
}
}
Una vez que .net 4.0 está disponible, puede usar Guid.TryParse()
.
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 .....
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:
/// <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);
}
}
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);
bool IsProbablyGuid(string s)
{
int hexchars = 0;
foreach(character c in string s)
{
if(IsValidHexChar(c))
hexchars++;
}
return hexchars==32;
}
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
- 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 compilada, de esa manera obtendrás exactamente el mismo comportamiento sin sobrecargar la excepción.
- ¿Constituye esto una ingeniería inversa? Creo que sí, y como tal podría ser ilegal.
- 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.