Question

Je veux essayer de convertir une chaîne en Guid, mais je ne veux pas compter sur la capture des exceptions (

  • pour des raisons de performances - les exceptions coûtent cher
  • pour des raisons de convivialité - le débogueur s’affiche
  • pour des raisons de conception - l'attendu n'est pas exceptionnel

En d'autres termes, le code:

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

ne convient pas.

Je voudrais essayer d’utiliser RegEx, mais comme le guid peut être entouré de parenthèses, l’accolade enveloppée, aucune encapsulée, rend la tâche difficile.

De plus, je pensais que certaines valeurs de guidage ne sont pas valides (?)

Mise à jour 1

ChristianK avait un C'est une bonne idée de ne capturer que FormatException , plutôt que tous. Modification de l'exemple de code de la question pour inclure une suggestion.

Mise à jour 2

Pourquoi s’inquiéter des exceptions levées? Est-ce que je m'attends vraiment si souvent à avoir des GUID invalides?

La réponse est oui . C’est pourquoi j’utilise TryStrToGuid - Je suis en attente de mauvaises données.

Exemple 1 Extensions d'espace de nom peut être spécifié en ajoutant un GUID à un nom de dossier . Je suis peut-être en train d’analyser les noms de dossiers et de vérifier si le texte après la dernière . est 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

Exemple 2 : un serveur Web très utilisé souhaite vérifier la validité de certaines données postées. Je ne veux pas que des données invalides bloquent les ressources de 2 à 3 ordres de grandeur plus élevés que nécessaire.

Exemple 3 Il se peut que j'analyse une expression de recherche entrée par un utilisateur.

entrer la description de l

S'ils saisissent des GUID, je souhaite les traiter spécifiquement (par exemple, rechercher spécifiquement cet objet ou mettre en surbrillance et mettre en forme ce terme de recherche spécifique dans le texte de la réponse.)

Mise à jour 3 - Critères de performance

Testez la conversion de 10 000 bons Guids et de 10 000 mauvais 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

p.s. Je ne devrais pas avoir à justifier une question.

Était-ce utile?

La solution

Critères de performance

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

Réponse Intertop COM (la plus rapide):

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

Conclusion: si vous avez besoin de vérifier si une chaîne est un guide et que vous vous souciez des performances, utilisez COM Interop.

Si vous devez convertir un GUID en représentation de chaîne en GUID, utilisez

.
new Guid(someString);

Autres conseils

Une fois que .net 4.0 est disponible, vous pouvez utiliser Guid.TryParse () .

Vous n'allez pas aimer ça, mais qu'est-ce qui vous fait penser qu'attraper l'exception va être plus lent?

Combien de tentatives infructueuses d'analyse d'un GUID attendez-vous par rapport aux tentatives réussies?

Mon conseil est d'utiliser la fonction que vous venez de créer et de profiler votre code. Si vous estimez que cette fonction est vraiment un point chaud , alors corrigez-la, mais pas avant.

Dans .NET 4.0, vous pouvez écrire comme suit:

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

Je voudrais au moins le réécrire comme suit:

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

Vous ne voulez pas dire "GUID non valide". sur SEHException, ThreadAbortException ou d’autres objets fatals ou non liés.

Mise à jour : à partir de .NET 4.0, un nouvel ensemble de méthodes est disponible pour Guid:

Vraiment, ceux-ci devraient être utilisés (ne serait-ce que pour le fait qu'ils ne sont pas "naïvement" mis en œuvre en utilisant try-catch en interne).

Interop est plus lent que la capture de l'exception:

Sur la bonne voie, avec 10 000 Guids:

Exception:    26ms
Interop:   1,201ms

Sur le chemin malheureux:

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

C'est plus cohérent, mais c'est aussi toujours plus lent. Il me semble que vous feriez mieux de configurer votre débogueur pour qu'il interrompt uniquement les exceptions non gérées.

Eh bien, voici la regex dont vous aurez besoin ...

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

Mais ce n'est que pour commencer. Vous devrez également vérifier que les différentes parties, telles que la date et l'heure, se situent dans les limites acceptables. Je ne peux pas imaginer que cela soit plus rapide que la méthode try / catch que vous avez déjà décrite. Espérons que vous ne recevrez pas autant de GUID non valides pour justifier ce type de vérification!

  

pour des raisons de convivialité - le débogueur s’affiche

Si vous optez pour l'approche try / catch, vous pouvez ajouter l'attribut [System.Diagnostics.DebuggerHidden] pour vous assurer que le débogueur ne se brise pas même si vous l'avez configuré pour le lancer.

S'il est vrai que l'utilisation des erreurs coûte plus cher, la plupart des gens pensent que la majorité de leurs GUID vont être générés par ordinateur. Un TRY-CATCH isn ' t trop cher car il ne génère que des coûts sur le CATCH . Vous pouvez vous en convaincre par un simple test de deux (utilisateur public, pas de mot de passe).

Voilà:

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

J'avais une situation similaire et j'ai remarqué que la chaîne invalide n'avait presque jamais 36 caractères. C’est pour cette raison que j’ai un peu modifié votre code pour obtenir de meilleures performances tout en restant 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;
    }
}

Autant que je sache, il n'y a pas quelque chose comme Guid.TryParse dans mscrolib. Selon Source de référence, le type Guid a un constructeur méga-complexe qui vérifie tous les types de formats de guidage et tente de les analyser. Vous ne pouvez pas appeler de méthode d'assistance, même par réflexion. Je pense que vous devez rechercher des analyseurs syntaxiques tiers ou écrire le vôtre.

Exécutez le GUID potentiel via un RegEx ou un code personnalisé qui effectue une vérification de cohérence pour vous assurer que le strig ressemble au moins à un GUID et ne contient que des caractères valides (et peut-être qu'il semble correspondre au format global). Si le contrôle d'intégrité échoue, une erreur est renvoyée: la grande majorité des chaînes non valides seront éliminées.

Ensuite, convertissez la chaîne comme vous l'avez décrit ci-dessus, en capturant toujours l'exception pour les quelques chaînes non valides validées.

Jon Skeet a analysé quelque chose de similaire pour l'analyse d'Ints (avant que TryParse ne se trouve dans Framework):

Toutefois, comme AnthonyWJones ont indiqué que vous ne devriez probablement pas vous inquiéter à ce sujet.

 bool IsProbablyGuid(string s)
    {
        int hexchars = 0;
        foreach(character c in string s)
        {
           if(IsValidHexChar(c)) 
               hexchars++;          
        }
        return hexchars==32;
    }
  • Obtenir le réflecteur
  • copy'n'paste Guid's .ctor (String)
  • remplacez chaque occurrence de " lancer un nouveau ... " avec "return false".

Le ctor de Guid est à peu près une expression rationnelle compilée, de cette façon, vous obtiendrez exactement le même comportement sans la surcharge de l'exception.

  1. Cela constitue-t-il une ingénierie inverse? Je pense que oui, et en tant que tel pourrait être illégal.
  2. Se cassera si le formulaire GUID change.

Même une solution plus froide consisterait à instrumenter dynamiquement une méthode, en remplaçant "jeter par un nouveau". à la volée.

Je vote pour le lien GuidTryParse posté ci-dessus par Jon ou une solution similaire (IsProbablyGuid). Je vais en écrire un comme celui de ma bibliothèque de conversion.

Je pense que le fait que cette question soit si compliquée est totalement boiteux. Le " est " ou " comme " le mot clé serait très bien SI un Guid pourrait être nul. Mais pour quelque raison que ce soit, même si SQL Server convient, mais pas .NET. Pourquoi? Quelle est la valeur de Guid.Empty? C’est juste un problème idiot créé par la conception de .NET, et cela m’embête vraiment lorsque les conventions d’un langage s’appliquent. La réponse la plus performante à ce jour a été l'utilisation de COM Interop car le framework ne le gère pas correctement. "Cette chaîne peut-elle être un GUID?" devrait être une question facile à répondre.

Il est correct de s’appuyer sur l’exception levée, jusqu’à ce que l’application passe sur Internet. À ce stade, je viens de me préparer à une attaque par déni de service. Même si je ne me fais pas "attaquer", je sais que certains yahoo vont sonner avec l'URL, ou peut-être que mon service marketing enverra un lien malformé, et que mon application subira une performance assez lourde qui pourrait provoquer mettre le serveur hors service parce que je n’ai pas écrit mon code pour traiter un problème qui NE DEVRAIT PAS se produire, mais nous savons tous que cela se produira.

La ligne est un peu floue sur " Exception " - Mais au bout du compte, même si le problème est peu fréquent, s’il peut se produire suffisamment de fois en un rien de temps pour que votre application se bloque, alors je pense que lancer une exception est une mauvaise forme.

TheRage3K

si TypeOf ctype (myvar, Object) est Guid alors .....

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

Avec une méthode d'extension en C #

public static bool IsGUID(this string text)
{
    return Guid.TryParse(text, out Guid guid);
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top