Tester si la chaîne est un guid sans lancer d’exceptions?

Je veux essayer de convertir une chaîne de caractères en Guid, mais je ne veux pas compter sur les exceptions de capture (

  • pour des raisons de performance – les exceptions sont chères
  • pour des raisons d’utilisabilité – le débogueur apparaît
  • pour des raisons de conception – ce qui est attendu n’est pas exceptionnel

En d’autres termes, le code:

public static Boolean TryStrToGuid(Ssortingng 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 comme le guid peut être placé entre parenthèses, il est difficile d’envelopper les accolades, sans les emballer.

De plus, je pensais que certaines valeurs Guid étaient invalides (?)


Mise à jour 1

ChristianK avait une bonne idée de ne prendre que FormatException , plutôt que toutes. Modification de l’exemple de code de la question pour inclure la suggestion.


Mise à jour 2

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

La réponse est oui . C’est pourquoi j’utilise TryStrToGuid – je m’attends à de mauvaises données.

Exemple 1 Les 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’parsingr les noms de dossiers, en vérifiant si le texte après la finale . 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 Je suis peut-être en train d’exécuter un serveur Web très utilisé qui veut vérifier la validité de certaines données postées. Je ne veux pas que les données invalides mettent les ressources en ordre de deux à trois fois plus que nécessaire.

Exemple 3 Je pourrais parsingr une expression de recherche entrée par un utilisateur.

entrer la description de l'image ici

S’ils entrent dans un GUID, je souhaite les traiter spécialement (comme rechercher spécifiquement cet object, ou mettre en évidence et mettre en forme ce terme de recherche spécifique dans le texte de la réponse).


Mise à jour 3 – Critères de performance

Test de conversion de 10 000 bons Guids et 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 CLSIDFromSsortingng 10,000 good: 126,120 ticks 10,000 bad: 23,134 ticks 

ps Je ne devrais pas avoir à justifier une question.

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 CLSIDFromSsortingng 10,000 good: 126,120 ticks 10,000 bad: 23,134 ticks 

COM Intertop (le plus rapide) Réponse:

 ///  /// Attempts to convert a ssortingng to a guid. ///  /// The ssortingng to try to convert /// Upon return will contain the Guid /// Returns true if successful, otherwise false public static Boolean TryStrToGuid(Ssortingng s, out Guid value) { //ClsidFromSsortingng returns the empty guid for null ssortingngs if ((s == null) || (s == "")) { value = Guid.Empty; return false; } int hresult = PInvoke.ObjBase.CLSIDFromSsortingng(s, out value); if (hresult >= 0) { return true; } else { value = Guid.Empty; return false; } } namespace PInvoke { class ObjBase { ///  /// This function converts a ssortingng generated by the SsortingngFromCLSID function back into the original class identifier. ///  /// Ssortingng 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 CLSIDFromSsortingng(ssortingng sz, out Guid clsid); } } 

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

Si vous avez besoin de convertir un GUID dans une représentation Ssortingng en Guid, utilisez

 new Guid(someSsortingng); 

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

Vous n’allez pas aimer cela, mais qu’est-ce qui vous fait penser que la capture de l’exception va être plus lente?

Combien de tentatives d’parsing d’un GUID ont échoué par rapport aux tentatives réussies?

Mon conseil est d’utiliser la fonction que vous venez de créer et de définir votre code. Si vous trouvez que cette fonction est vraiment un hotspot, corrigez-la mais pas avant.

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

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

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 invalide” sur SEHException, ThreadAbortException ou autres choses fatales ou non liées.

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

  • Guid.TryParse
  • Guid.TryParseExact

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

Interop est plus lent que la simple capture de l’exception:

Dans le bon chemin, avec 10.000 Guids:

 Exception: 26ms Interop: 1,201ms 

Dans le malheureux chemin:

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

C’est plus cohérent, mais c’est aussi plus lent. Il me semble que vous feriez mieux de configurer votre débogueur pour ne casser que 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 c’est juste pour commencer. Vous devrez également vérifier que les différentes parties, telles que la date et l’heure, se situent dans des plages 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 recevez pas autant de GUID invalides pour garantir ce type de vérification!

pour des raisons d’utilisabilité – le débogueur apparaît

Si vous optez pour l’approche try / catch, vous pouvez append l’atsortingbut [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 est plus coûteuse, la plupart des gens croient que la majorité de leurs GUID seront générés par ordinateur, de sorte qu’un TRY-CATCH n’est pas trop coûteux puisqu’il ne génère que des coûts sur le CATCH . Vous pouvez le prouver par un simple test des deux (utilisateur public, pas de mot de passe).

Voici:

 using System.Text.RegularExpressions; ///  /// Validate that a ssortingng is a valid GUID ///  ///  ///  private bool IsValidGUID(ssortingng GUIDCheck) { if (!ssortingng.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; } 

J’ai eu une situation similaire et j’ai remarqué que la chaîne invalide ne comportait presque jamais 36 caractères. Sur cette base, j’ai un peu changé votre code pour obtenir de meilleures performances tout en restant simple.

 public static Boolean TryStrToGuid(Ssortingng 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; } } 

Pour autant que je sache, il n’y a pas quelque chose comme Guid.TryParse dans mscrolib. Selon la source de référence, le type Guid a un constructeur méga-complexe qui vérifie toutes sortes de formats guidés et essaie de les parsingr. Il n’y a pas de méthode d’assistance que vous pouvez appeler, même par reflection. Je pense que vous devez rechercher des assistants de guidage tiers ou écrire les vôtres.

Exécutez le GUID potentiel via un RegEx ou un code personnalisé qui effectue un test de cohérence pour vous assurer que la ssortingg 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 ne réussit pas le test de validité, retournez une erreur – cela éliminera probablement la grande majorité des chaînes non valides.

Convertissez ensuite la chaîne comme ci-dessus, en capturant toujours l’exception pour les quelques chaînes non valides qui passent la vérification de la validité.

Jon Skeet a fait une parsing pour quelque chose de similaire pour l’parsing d’Ints (avant que TryParse ne soit dans le Framework): Vérifier si une chaîne peut être convertie en Int32

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

  bool IsProbablyGuid(ssortingng s) { int hexchars = 0; foreach(character c in ssortingng s) { if(IsValidHexChar(c)) hexchars++; } return hexchars==32; } 
  • Obtenez un réflecteur
  • copy’n’paste Guid’s .ctor (Ssortingng)
  • remplacer toutes les occurrences de “Lancer nouveau …” par “Renvoyer fausse”.

Le ctor de Guid est quasiment 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 le formulaire GUID change.

Une solution encore plus intéressante consisterait à instrumentaliser dynamicment une méthode, en remplaçant “jeter de nouveau” à la volée.

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

Je pense qu’il est totalement nul que cette question soit si compliquée. Le mot clé “is” ou “as” serait correct si un Guid pouvait être nul. Mais pour une raison quelconque, même si SQL Server est compatible avec cela, .NET ne l’est pas. Pourquoi? Quelle est la valeur de Guid.Empty? Ceci est juste un problème stupide créé par la conception de .NET, et cela me gêne vraiment lorsque les conventions d’un langage montent sur lui-même. La meilleure réponse à ce jour a été d’utiliser 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.

S’appuyer sur l’exception à lancer est acceptable jusqu’à ce que l’application soit mise sur Internet. À ce stade, je me suis juste préparé à une attaque par déni de service. Même si je ne suis pas “attaqué”, je sais que certains vont à singe avec l’URL, ou peut-être que mon département marketing enverra un lien mal formé, et que ma candidature devra subir un impact assez lourd qui pourrait le serveur parce que je n’ai pas écrit mon code pour gérer un problème qui ne devrait pas se produire, mais nous soaps tous que va arriver.

Cela brouille un peu la ligne sur “Exception” – mais en bout de ligne, même si le problème est rare, si cela peut arriver assez souvent dans un laps de temps court pour que votre application tombe en panne. mauvaise forme.

TheRage3K

si TypeOf type (myvar, Object) est Guid alors …..

 Private Function IsGuidWithOptionalBraces(ByRef strValue As Ssortingng) As Boolean If Ssortingng.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 Ssortingng) As Boolean If Ssortingng.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 Ssortingng) As Boolean If Ssortingng.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 

Avec une méthode d’extension en C #

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