185 votes

Tester si une chaîne de caractères est un guide sans lancer d'exceptions ?

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

  • pour des raisons de performance - les exceptions sont coûteuses
  • 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 bien d'utiliser RegEx, mais comme le guidon peut être enveloppé de parenthèses, d'accolades ou non, c'est difficile.

De plus, je pensais que certaines valeurs Guid sont invalides ( ?).


Mise à jour 1

ChristianK a eu la bonne idée d'attraper seulement FormatException plutôt que tous. 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 GUIDs invalides si souvent ?

La réponse est oui . C'est pourquoi j'utilise TryStrToGuid. Je suis s'attendant à de mauvaises données.

Exemple 1 Les extensions d'espace de nommage peuvent être spécifiées en ajoutant un GUID au nom d'un dossier. . Je pourrais analyser les noms de dossiers, vérifier 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 Il se peut qu'un serveur web très utilisé veuille vérifier la validité de certaines données renvoyées. Je ne veux pas que des données non valides accaparent des ressources supérieures de 2 ou 3 ordres de grandeur à ce qu'elles devraient être.

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

enter image description here

S'ils saisissent des GUID, je veux les traiter spécialement (par exemple en recherchant spécifiquement cet objet, ou en mettant en évidence et en formatant 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 CLSIDFromString
   10,000 good:    126,120 ticks
   10,000 bad:      23,134 ticks

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

0 votes

Je ne connais pas la réponse à cette question, mais pour ce que ça vaut, vous avez raison de ne pas vouloir utiliser un bloc try/catch ici. Il faut beaucoup de calculs pour compenser le coût de la capture d'une exception et le bloc try/catch n'est pas fait pour le déroulement normal du programme ! Bien sûr, si vous ne devez pas attraper beaucoup d'exceptions dans ce code, ce n'est pas vraiment un problème .

7 votes

Pourquoi diable est-ce un wiki communautaire ?

36 votes

Vous avez raison, vous devriez no doivent justifier une question. Cependant, j'ai lu la justification avec intérêt (car elle est très similaire à la raison pour laquelle je suis ici en train de lire ceci). Donc, merci pour cette excellente justification.

110voto

Ian Boyd Points 50743

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

COM Intertop (le plus rapide) Réponse :

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

En résumé : Si vous avez besoin de vérifier si une chaîne est un guid et que vous vous souciez des performances, utilisez COM Interop.

Si vous avez besoin de convertir un guid en représentation String en un Guid, utilisez

new Guid(someString);

9 votes

Les avez-vous exécutés avec le débogueur activé ou désactivé ? La performance de la levée d'exception est améliorée de plusieurs fois sans attacher le débogueur.

0 votes

Merci. J'étais sur le point de poser cette question moi-même. Je suis heureux d'avoir trouvé votre réponse.

0 votes

J'ai créé un nouveau fichier appelé PInvoke.cs avec l'extrait de code de l'espace de noms PInvoke ci-dessus, mais je n'arrive pas à faire fonctionner le code. Lorsque je débogue, je constate que le résultat de CLSIDFromString est TOUJOURS négatif. J'ai essayé de changer la ligne d'appel en : int hresult = PInvoke.ObjBase.CLSIDFromString(Guid.NewGuid().ToString(), out value) ; mais il est toujours négatif. Qu'est-ce que je fais de mal ?

91voto

Une fois que .net 4.0 sera disponible, vous pourrez utiliser Guid.TryParse() .

9 votes

Un moyen encore plus rapide est d'utiliser la méthode Guid.TryParseExact().

6 votes

Si l'analyse des chaînes Guid est la partie la plus lente de votre application, alors vous êtes béni.

67voto

AnthonyWJones Points 122520

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

Combien de tentatives d'analyse d'un GUID échouent 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 trouvez que cette fonction est vraiment un point chaud puis le réparer, mais pas avant.

2 votes

Bonne réponse, l'optimisation prématurée est la racine de tous les maux.

34 votes

Il est malvenu de se fier à des exceptions qui ne sont pas exceptionnelles. C'est une mauvaise habitude que je ne voudrais pas que quiconque prenne. Et je ne voudrais surtout pas le faire dans une routine de bibliothèque où les gens auront confiance que cela fonctionne bien.

0 votes

Anonyme, votre question initiale mentionnait les performances comme la raison pour laquelle vous vouliez éviter les exceptions. Si ce n'est pas le cas, vous devriez peut-être modifier votre question.

43voto

zhilia Points 1

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

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

4 votes

Cela devrait vraiment être l'une des premières réponses.

22voto

Christian.K Points 18883

Je le réécrirais au moins comme suit :

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

Vous ne voulez pas dire "GUID invalide" lors d'une SEHException, d'une ThreadAbortException ou d'autres choses fatales ou non liées.

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

Il faut vraiment les utiliser (ne serait-ce que pour le fait qu'elles ne sont pas implémentées "naïvement" en utilisant try-catch en interne).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X