web-dev-qa-db-fra.com

Vérifier si la chaîne est un guid sans lancer d'exceptions?

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

  • pour des raisons de performances - les exceptions coûtent cher
  • pour des raisons de convivialité - le débogueur apparaît
  • 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.

J'essaierais d'utiliser RegEx, mais puisque le guid peut être enveloppé de parenthèses, enveloppé d'une accolade, aucun enveloppé, rend la tâche difficile.

De plus, je pensais que certaines valeurs Guid n'étaient pas valides (?)


Mise à jour 1

ChristianK a eu 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 jeté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 attend l'attente de données incorrectes.

Exemple 1 Des extensions d'espace de noms peuvent être spécifiées en ajoutant un GUID à un nom de dossier . Je suis peut-être en train d’analyser les noms de dossiers en vérifiant 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 J'utilise peut-être un serveur Web très utilisé qui 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 Je pourrais analyser une expression de recherche entrée par un utilisateur.

enter image description here

S'ils saisissent des GUID, je souhaite les traiter spécialement (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.

179
Ian Boyd

Benchmarks 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 COM Intertop (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 dans la représentation de chaîne en un guid, utilisez

new Guid(someString);
107
Ian Boyd

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

88

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’analyser un GUID attendez-vous par rapport à celles qui ont réussi?

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

66
AnthonyWJones

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

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

Je voudrais au moins le réécrire comme:

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

Vous ne voulez pas dire "GUID non valide" dans SEHException, ThreadAbortException ou d'autres éléments fatals ou non liés.

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

En réalité, ceux-ci devraient être utilisés (ne serait-ce que pour le fait qu'ils ne sont pas implémentés "naïvement" en utilisant try-catch en interne).

21
Christian.K

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

Sur le bon chemin, avec 10.000 Guids:

Exception:    26ms
Interop:   1,201ms

Dans 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 n'interrompe que les exceptions non gérées.

13
Mark Brackett

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. J'espère que vous ne recevrez pas autant de GUID non valides pour justifier ce type de vérification!

9
pdavis

pour des raisons de convivialité - le débogueur apparaît

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 casse pas même si vous l'avez configuré pour le lancer.

5
JMD

Bien que soit vrai que l’utilisation d’erreurs coûte plus cher, la plupart des gens pensent que la majorité de leurs GUID seront générés par ordinateur, donc un TRY-CATCH _ n'est pas trop cher puisqu'il génère uniquement des coûts sur le CATCH. Vous pouvez le prouver par un simple test de deux (utilisateur public, pas de mot de passe).

Voici:

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

J'ai eu une situation similaire et j'ai remarqué que la chaîne invalide n'a presque jamais été longue de 36 caractères. Donc, basé sur ce fait, j'ai légèrement 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;
    }
}
4
JBrooks

Exécutez le potentiel GUID si un RegEx ou un code personnalisé effectuant un contrôle de cohérence) permet de s'assurer que la strig ressemble au moins à un GUID et ne contient que des caractères valides). (et peut-être que cela semble correspondre au format global.) S'il échoue, le test d'intégrité renvoie une erreur, ce qui éliminera probablement la grande majorité des chaînes non valides.

Convertissez ensuite la chaîne comme vous l’avez indiqué ci-dessus, en prenant toujours l’exception pour les quelques chaînes non valides qui passent le contrôle de cohérence.

Jon Skeet a analysé quelque chose de similaire pour analyser Ints (avant que TryParse ne soit dans le Framework): Vérification de la possibilité de convertir une chaîne en Int32

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

2
Michael Burr

Autant que je sache, il n'y a pas quelque chose comme Guid.TryParse dans mscrolib. Selon Reference Source, 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 tiers de Guid ou écrire le vôtre.

2
Ilya Ryzhenkov
 bool IsProbablyGuid(string s)
    {
        int hexchars = 0;
        foreach(character c in string s)
        {
           if(IsValidHexChar(c)) 
               hexchars++;          
        }
        return hexchars==32;
    }
1
rupello
  • Obtenir un réflecteur
  • .ctor de copy'n'paste Guid (Chaîne)
  • remplacez chaque occurrence de "throw new ..." par "return false".

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

  1. Est-ce que cela constitue une ingénierie inverse? Je pense que oui, et en tant que tel pourrait être illégal.
  2. Se cassera si GUID change de forme.

Une solution encore plus froide consisterait à instrumenter de manière dynamique une méthode en remplaçant "lancer le nouveau" à la volée.

1
THX-1138

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 c'est totalement nul que cette question soit si compliquée. Le mot clé "est" ou "en tant que" conviendrait parfaitement SI un Guid pouvait être null. 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’une langue 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 à laquelle il est facile de répondre.

Compter sur l'exception, c'est correct, 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 utilisateurs de Yahoo vont utiliser l'URL, ou peut-être que mon service marketing enverra un lien malformé, et que mon application subira un coup assez lourd en termes de performances qui pourrait amener sur le serveur 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 ça va arriver.

Cela brouille un peu la ligne sur "Exception" - mais au fond, même si le problème est peu fréquent, si cela peut se produire suffisamment de fois en un temps record pour que votre application se bloque en utilisant toutes les captures, je pense que le lancement d'une exception est mauvaise forme.

TheRage3K

1
TheRage3K
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
0
Stefan Steiger

Avec une méthode d'extension en C #

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

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

0
mbm_tn