Domanda

Voglio provare a convertire una stringa in un Guid, ma non voglio fare affidamento sul rilevamento delle eccezioni (

  • per motivi di prestazioni - le eccezioni sono costose
  • per motivi di usabilità - si apre il debugger
  • per motivi di progettazione - il previsto 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 poiché il guid può essere racchiuso tra parentesi, impacchettato, nessuno avvolto, lo rende difficile.

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


Aggiornamento 1

ChristianK aveva un buona idea catturare solo FormatException , piuttosto che tutti. Modificato l'esempio di codice della domanda per includere suggerimenti.


Aggiornamento 2

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

La risposta è . Ecco perché sto usando TryStrToGuid - sto mi aspetto dati errati.

Esempio 1 Estensioni dello spazio dei nomi può essere specificato aggiungendo un GUID a un nome di cartella . Potrei analizzare i nomi delle cartelle, verificando se il testo dopo l'ultimo . è 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 molto utilizzato vuole verificare la validità di alcuni dati postati. Non voglio dati non validi che leghino le risorse 2-3 ordini di grandezza più alti di quanto debbano essere.

Esempio 3 Potrei analizzare un'espressione di ricerca immessa da un utente.

inserisci qui la descrizione dell

Se immettono i GUID, voglio elaborarli in modo speciale (come cercare specificamente quell'oggetto, oppure evidenziare e formattare quel termine di ricerca specifico nel testo della risposta.)


Aggiornamento 3 - benchmark delle prestazioni

Prova a convertire 10.000 guide valide e 10.000 guide non valide.

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

È stato utile?

Soluzione

Benchmark prestazionali

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

Risposta intertop (più veloce) COM:

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

In conclusione: se devi controllare se una stringa è un guid e ti preoccupi delle prestazioni, usa l'interoperabilità COM.

Se devi convertire un guid nella rappresentazione String in un Guid, usa

new Guid(someString);

Altri suggerimenti

Una volta disponibile .net 4.0 è possibile 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 riusciti?

Il mio consiglio è usare la funzione che hai appena creato e creare il profilo del tuo codice. Se trovi che questa funzione è davvero un hotspot allora risolvilo ma non prima.

In .NET 4.0 è possibile 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 vuoi dire " GUID non valido " su SEHException, ThreadAbortException o altre cose fatali o non correlate.

Aggiorna : a partire da .NET 4.0, è disponibile una nuova serie di metodi per Guid:

In realtà, quelli dovrebbero essere usati (se non altro per il fatto, che non sono "ingenuamente" implementati usando try-catch internamente).

L'interoperabilità è più lenta della sola eccezione dell'eccezione:

Nel percorso felice, con 10.000 guide:

Exception:    26ms
Interop:   1,201ms

Nel percorso infelice:

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

È più coerente, ma è anche più lento. Mi sembra che sarebbe meglio configurare il debugger per interrompere solo 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 cominciare. Dovrai anche verificare che le varie parti come la data / ora siano comprese in intervalli accettabili. Non riesco a immaginare che questo sia più veloce del metodo try / catch che hai già delineato. Spero che tu non stia ricevendo così tanti GUID non validi per giustificare questo tipo di controllo!

  

per motivi di usabilità - si apre il debugger

Se stai andando per l'approccio try / catch puoi aggiungere l'attributo [System.Diagnostics.DebuggerHidden] per assicurarti che il debugger non si rompa anche se l'hai impostato su break on lancio.

/ p>

Mentre è vero che l'uso degli errori è più costoso, la maggior parte delle persone crede che la maggior parte dei loro GUID sarà generata da computer, quindi un TRY-CATCH non è ' è troppo costoso poiché genera solo costi sul CATCH . Puoi provarlo con un semplice test dei due (utente pubblico, nessuna password).

Ecco qua:

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})<*>quot;).IsMatch(GUIDCheck);
   }
   return false;
  }

Ho avuto una situazione simile e ho notato che quasi mai la stringa non valida era lunga 36 caratteri. Quindi, basandomi su questo fatto, ho modificato un po 'il tuo codice per ottenere prestazioni migliori pur rimanendo semplice.

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 esiste qualcosa come Guid.TryParse in mscrolib. Secondo la fonte di riferimento, 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 è possibile chiamare, anche tramite la riflessione. Penso che devi cercare parser Guid di terze parti o scrivere il tuo.

Esegui il potenziale GUID attraverso un RegEx o un codice personalizzato che esegua un controllo di integrità per garantire che la stringa almeno assomigli a un GUID e sia composta 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 stragrande maggioranza delle stringhe non valide.

Quindi converti la stringa come hai sopra, ancora catturando 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): AnthonyWJones ha indicato che 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)
  • sostituisci ogni occasione di " lancia nuovo ... " con "return false".

Il ctor 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 di si, e come tale potrebbe essere illegale.
  2. Si interromperà se cambia il modulo GUID.

Una soluzione ancora più interessante sarebbe quella di strumentare dinamicamente un metodo, sostituendo "lanciare nuovo" al volo.

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. "Quot" è " o " come " la parola chiave andrebbe bene SE un Guid potrebbe essere nullo. Ma per qualche ragione, anche se SQL Server è d'accordo, .NET non lo è. Perché? Qual è il valore di Guid.Empty? Questo è solo un problema sciocco creato dalla progettazione di .NET, e mi dà davvero fastidio quando le convenzioni di un linguaggio calpestano se stesso. La risposta più efficace finora è stata l'utilizzo dell'interoperabilità COM perché Framework non la gestisce con garbo? " Può questa stringa essere un GUID? " dovrebbe essere una domanda a cui è facile rispondere.

Fare affidamento sull'eccezione generata è OK, fino a quando l'app non passa su Internet. A quel punto mi sono appena preparato per un attacco denial of service. Anche se non ottengo "attaccato", so che alcuni yahoo si scervellano con l'URL, o forse il mio dipartimento marketing invierà un link non valido, e quindi la mia applicazione deve subire un forte calo delle prestazioni che POTREBBE far cadere il server perché non ho scritto il mio codice per gestire un problema che NON DOVREBBE accadere, ma sappiamo tutti che ACCADRÀ.

Ciò sfoca un po 'la linea su " Eccezione " - ma la linea di fondo, anche se il problema è raro, se può accadere abbastanza volte in un breve lasso di tempo che l'applicazione si blocca durante la manutenzione delle catture da tutto, allora penso che lanciare un'eccezione sia una forma sbagliata.

TheRage3K

se TypeOf ctype (myvar, Object) Is 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}[\}]?<*>quot;, 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}<*>quot;, 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}\}<*>quot;, 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);
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top