Verifica se string è un guid senza generare eccezioni?

Voglio provare a convertire una stringa in una guida, ma non voglio fare affidamento sull’ottenere eccezioni (

  • per motivi di prestazioni – le eccezioni sono costose
  • per motivi di usabilità, compare il debugger
  • per motivi di design – l’attesa non è eccezionale

In altre parole il codice:

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

non è adatto.

Proverei ad usare RegEx, ma dal momento che il guid può essere racchiuso tra parentesi, avvolto, non avvolto, rende difficile.

Inoltre, ho pensato che alcuni valori di Guid non sono validi (?)


Aggiornamento 1

ChristianK ha avuto una buona idea di prendere solo FormatException , piuttosto che tutto. Modificato l’esempio del codice della domanda per includere il suggerimento.


Aggiornamento 2

Perché preoccuparsi delle eccezioni generate? Mi aspetto davvero molto spesso GUID non validi?

La risposta è Questo è il motivo per cui sto usando TryStrToGuid – Mi aspetto dei cattivi dati.

Esempio 1 Le estensioni dello spazio dei nomi possono essere specificate aggiungendo un GUID al nome di una cartella . Potrei analizzare i nomi delle cartelle, controllando se il testo dopo la finale . è 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 

Esempio 2 Potrei essere in esecuzione un web server pesantemente utilizzato vuole verificare la validità di alcuni dati di back pubblicati. Non voglio dati non validi che legano risorse di 2-3 ordini di grandezza più alti di quanto non sia necessario.

Esempio 3 Potrei analizzare l’espressione di ricerca inserita da un utente.

inserisci la descrizione dell'immagine qui

Se inseriscono i GUID, desidero elaborarli in modo specifico (come ad esempio la ricerca specifica di quell’object o evidenziare e formattare quel termine specifico di ricerca nel testo della risposta).


Aggiornamento 3: benchmark delle prestazioni

Prova a convertire 10.000 Guidi buoni e 10.000 Guidi cattivi.

 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 Non dovrei giustificare una domanda.

Benchmark delle prestazioni

 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 (più veloce) Risposta:

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

In conclusione: se è necessario verificare se una stringa è un guid e si preoccupa delle prestazioni, utilizzare COM Interop.

Se è necessario convertire un guid in rappresentazione String in un Guid, utilizzare

 new Guid(someString); 

Una volta disponibile .net 4.0 è ansible utilizzare Guid.TryParse() .

Non ti piacerà questo, ma cosa ti fa pensare che catturare l’eccezione sarà più lento?

Quanti tentativi falliti di analizzare un GUID ti aspetti rispetto a quelli di successo?

Il mio consiglio è usare la funzione che hai appena creato e profilare il tuo codice. Se trovi che questa funzione è davvero un hotspot, risolvila ma non prima.

In .NET 4.0 puoi scrivere come segue:

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

Lo riscriverei almeno come:

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

Non si vuole dire “GUID non valido” su SEHException, ThreadAbortException o altre cose fatali o non correlate.

Aggiornamento : a partire da .NET 4.0, è disponibile un nuovo set di metodi per Guid:

  • Guid.TryParse
  • Guid.TryParseExact

In realtà, questi dovrebbero essere usati (se non altro per il fatto che non vengono implementati “ingenuamente” usando try-catch internamente).

L’interoperabilità è più lenta rispetto alla semplice eccezione:

Nel felice percorso, con 10.000 Guids:

 Exception: 26ms Interop: 1,201ms 

Nel percorso infelice:

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

È più coerente, ma è anche costantemente più lento. Mi sembra che sarebbe meglio configurare il tuo debugger solo per rompere le eccezioni non gestite.

Bene, ecco la regex di cui avrai bisogno …

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

Ma questo è solo per i principianti. Dovrai inoltre verificare che le varie parti come la data / l’ora rientrino in intervalli accettabili. Non riesco a immaginare che sia più veloce del metodo try / catch che hai già delineato. Si spera che non si ricevano molti GUID non validi per garantire questo tipo di controllo!

per motivi di usabilità, compare il debugger

Se stai cercando l’approccio try / catch, puoi aggiungere l’attributo [System.Diagnostics.DebuggerHidden] per assicurarti che il debugger non si interrompa anche se lo hai impostato per l’interruzione al lancio.

Se è vero che l’uso di errori è più costoso, molte persone credono che la maggior parte dei loro GUID saranno generati dal computer, quindi un TRY-CATCH non è troppo costoso poiché genera solo costi sul CATCH . Puoi dimostrarlo a te stesso con un semplice test dei due (utente pubblico, senza password).

Ecco qui:

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

Ho avuto una situazione simile e ho notato che quasi mai la stringa non valida era lunga 36 caratteri. Quindi, in base a questo fatto, ho modificato il tuo codice un po ‘per ottenere prestazioni migliori pur mantenendo la semplicità.

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

Per quanto ne so, non c’è qualcosa come Guid.TryParse in mscrolib. Secondo la Reference Source, il tipo Guid ha un costruttore mega-complesso che controlla tutti i tipi di formati guid e cerca di analizzarli. Non esiste un metodo di supporto che puoi chiamare, anche tramite riflessione. Penso che tu debba cercare i parser di terze parti Guid o scriverne uno tuo.

Esegui il potenziale GUID attraverso una RegEx o un codice personalizzato che esegua un controllo di integrità per garantire che lo strig assomigli almeno a un GUID e che sia composto solo da caratteri validi (e forse sembra che si adatti al formato generale). Se non supera il controllo di integrità restituisce un errore – probabilmente eliminerà la maggior parte delle stringhe non valide.

Quindi converti la stringa come sopra, ottenendo comunque l’eccezione per le poche stringhe non valide che superano il controllo di integrità.

Jon Skeet ha fatto un’analisi per qualcosa di simile per l’analisi di Ints (prima che TryParse fosse nel Framework): Verifica se una stringa può essere convertita in Int32

Tuttavia, come indicato da AnthonyWJones , probabilmente non dovresti preoccuparti di questo.

  bool IsProbablyGuid(string s) { int hexchars = 0; foreach(character c in string s) { if(IsValidHexChar(c)) hexchars++; } return hexchars==32; } 
  • Ottieni Reflector
  • copy’n’paste Guid’s .ctor (String)
  • sostituire ogni occorrenza di “lanciare nuovi …” con “return false”.

Il codice di Guid è praticamente una regex compilata, in questo modo otterrai esattamente lo stesso comportamento senza sovraccarico dell’eccezione.

  1. Questo costituisce un reverse engineering? Penso che lo faccia e in quanto tale potrebbe essere illegale.
  2. Si interromperà se il modulo GUID cambia.

Una soluzione ancora più interessante sarebbe quella di strumentare dynamicmente un metodo, sostituendo “lancia nuovi” al volo.

Io voto per il link GuidTryParse pubblicato sopra da Jon o una soluzione simile (IsProbablyGuid). Scriverò uno come quelli per la mia libreria di conversione.

Penso che sia totalmente zoppo che questa domanda debba essere così complicata. La parola chiave “è” o “come” andrebbe bene SE un Guid potrebbe essere nullo. Ma per qualche ragione, anche se SQL Server è OK, .NET no. Perché? Qual è il valore di Guid.Empty? Questo è solo un problema sciocco creato dal design di .NET, e mi infastidisce davvero quando le convenzioni di un linguaggio si fanno avanti. La risposta più efficace finora è stata l’utilizzo di COM Interop perché il Framework non lo gestisce con garbo? “Questa stringa può essere un GUID?” dovrebbe essere una domanda facile da rispondere

Fare affidamento sull’eccezione generata è OK, fino a quando l’app non sarà disponibile su Internet. A quel punto mi sono appena presentato per un attacco denial of service. Anche se non vengo “attaccato”, so che yahoo sta per scimmiottare l’URL, o forse il mio dipartimento marketing invierà un link malformato, e quindi la mia applicazione deve subire un colpo di performance abbastanza pesante che potrebbe portare giù il server perché non ho scritto il mio codice per gestire un problema che NON dovrebbe accadere, ma lo sappiamo tutti POTREBBE ACCADERE.

Ciò offusca un po ‘la riga su “Exception” – ma in fondo, anche se il problema non è frequente, se può succedere abbastanza volte in un breve intervallo di tempo in cui l’applicazione si arresta in modo anomalo servendo i catch da tutto, quindi penso che lanciare un’eccezione è ctriggers forma.

TheRage3K

se TypeOf ctype (myvar, Object) è Guid allora …..

 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 metodo di estensione in C #

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