Question

J'ai plusieurs méthodes similaires, par exemple. CalculatePoint (...) et CalculateListOfPoints (...). Parfois, ils peuvent ne pas réussir et doivent l'indiquer à l'appelant. Pour CalculateListOfPoints, qui retourne une liste générique, je pourrais retourner une liste vide et demander à l'appelant de vérifier cela; cependant Point est un type de valeur et je ne peux donc pas renvoyer null ici.

Idéalement, j'aimerais que les méthodes «ressemblent»; une solution pourrait être de les définir comme

public Point CalculatePoint(... out Boolean boSuccess);
public List<Point> CalculateListOfPoints(... out Boolean boSuccess);

ou alternativement pour retourner un point? pour CalculatePoint et renvoie la valeur null pour indiquer un échec. Cela impliquerait toutefois de revenir au type non nullable, ce qui semble excessif.

Une autre route consisterait à renvoyer le boSuccess booléen, à avoir le résultat (Point ou List) comme paramètre "out" et à l'appeler TryToCalculatePoint ou quelque chose du genre ...

Quelle est la meilleure pratique?

Edit: je ne souhaite pas utiliser Exceptions pour le contrôle de flux! Un échec est parfois attendu.

Était-ce utile?

La solution

Personnellement, je pense que j'utiliserais la même idée que TryParse (): utiliser un paramètre out pour générer la valeur réelle et renvoyer un booléen indiquant si l'appel a abouti ou non

public bool CalculatePoint (... résultat Out Point);

Je ne suis pas partisan d'utiliser l'exception pour "normal". comportements (si vous vous attendez à ce que la fonction ne fonctionne pas pour certaines entrées).

Autres conseils

Pourquoi échoueraient-ils? Si c'est à cause de quelque chose que l'appelant a fait (c'est-à-dire les arguments fournis), alors lancer ArgumentException est tout à fait approprié. Une méthode Try [...] qui évite les exceptions convient parfaitement.

Je pense que c’est une bonne idée de fournir la version qui jette une exception, afin que les appelants qui s’attendent à fournir de bonnes données reçoivent un message suffisamment fort (c.-à-d. une exception) s’ils se trompent un jour. / p>

Une autre alternative consiste à lancer une exception. Cependant, vous ne voulez généralement lancer des exceptions que dans des "cas exceptionnels".

Si les cas d'échec sont courants (et non exceptionnels), vous avez déjà indiqué vos deux options. MODIFIER : il peut exister une convention dans votre projet sur la manière de traiter de tels cas non exceptionnels (qu'il s'agisse de renvoyer la réussite ou l'objet). S'il n'y a pas de convention existante, je suis d'accord avec lucasbfr et je vous suggère de renvoyer le succès (ce qui est conforme à la conception de TryParse (...)).

Si l’échec est dû à une raison spécifique, je pense que son droit de retourner null ou bool et d’avoir un paramètre out. Si toutefois vous renvoyez null indépendamment de l'échec, je ne le recommande pas. Les exceptions fournissent un riche ensemble d'informations, y compris la raison pourquoi quelque chose a échoué. Si tout ce que vous récupérez est nul, comment savoir si c'est parce que les données sont fausses, que vous n'avez plus de mémoire vive ou qu'un autre comportement étrange?

Même dans .net, TryParse a un frère Parse afin que vous puissiez obtenir l'exception si vous le souhaitez.

Si je fournissais une méthode TrySomething, je fournirais également une méthode Something qui lève une exception en cas d'échec. Ensuite, c'est à l'appelant de décider.

Le modèle que j'ai utilisé est le même que celui utilisé par MS avec les méthodes TryParse de différentes classes.

Votre code d'origine:
point public CalculatePoint (... sur boSuccess booléen);
liste publique CalculateListOfPoints (... sur boSuccess booléen);

se transformerait en public bool CalculatePoint (... out (ou ref Point Point CalculatedValue);
public bool CalculateListOfPoints (... out (ou ref) List CalculatedValues);

Fondamentalement, vous faites de la réussite / de l'échec la valeur de retour.

Pour résumer, vous pouvez adopter plusieurs approches:

  1. Lorsque le type de retour est un type de valeur, comme Point, utilisez la fonctionnalité Nullable de C # et retournez un point? (alias Nullable), de cette façon, vous pouvez toujours retourner null en cas d'échec
  2. Lance une exception en cas d'échec. Toute la discussion / discussion concernant ce qui est et n’est pas "exceptionnel" est un point discutable, c'est votre API, vous décidez quel est le comportement exceptionnel.
  3. Adoptez un modèle similaire à celui implémenté par Microsoft dans les types de base tels que Int32, fournissez un CalculatePoint et TryCalculatePoint (int32.Parse et int32.TryParse) et obtenez un lancer et un autre un bool.
  4. Renvoyez une structure générique de vos méthodes ayant deux propriétés, bool Success et GenericType Value.

En fonction du scénario, j'ai tendance à combiner le renvoi nul ou le lancement d'une exception car ils semblent "les plus propres". pour moi et correspond le mieux avec le code existant à la société pour laquelle je travaille. Ma meilleure pratique personnelle serait donc les approches 1 et 2.

Cela dépend principalement du comportement de vos méthodes et de leur utilisation.

Si l'échec est commun et non critique, demandez à vos méthodes de renvoyer un booléen indiquant leur succès et utilisez un paramètre out pour transmettre le résultat. Rechercher une clé dans un hachage, tenter de lire des données sur un socket non bloquant lorsqu'aucune donnée n'est disponible, tous ces exemples appartiennent à cette catégorie.

Si un échec est inattendu, retournez directement le résultat et transmettez les erreurs avec des exceptions. L’ouverture d’un fichier en lecture seule et la connexion à un serveur TCP sont de bons candidats.

Et parfois, les deux sens ont un sens ...

Renvoyez Point.Empty . C'est un modèle de conception .NET qui renvoie un champ spécial lorsque vous voulez vérifier si la création de structure a réussi. Évitez les paramètres out lorsque vous le pouvez.

public static readonly Point Empty

Un modèle que je teste renvoie un Peut-être . Il a la sémantique du modèle TryParse , mais une signature similaire à celle du modèle null-return-on-error.

Je ne suis pas encore convaincu d’une manière ou d’une autre, mais je l’offre pour votre considération collective. Il présente l'avantage de ne pas nécessiter qu'une variable soit définie avant l'appel de la méthode pour que le paramètre out soit conservé sur le site d'appel de la méthode. Il pourrait également être étendu avec une collection Erreurs ou Messages pour indiquer la raison de l'échec.

La classe Maybe ressemble à ceci:

/// <summary>
/// Represents the return value from an operation that might fail
/// </summary>
/// <typeparam name="T"></typeparam>
public struct Maybe<T>
{
    T _value;
    bool _hasValue;


    public Maybe(T value)
    {
        _value = value;
        _hasValue = true;
    }

    public Maybe()
    {
        _hasValue = false;
        _value = default(T);
    }


    public bool Success
    {
        get { return _hasValue; }
    }


    public T Value
    {
        get 
            { // could throw an exception if _hasValue is false
              return _value; 
            }
    }
}

Je dirais que les meilleures pratiques sont une valeur de retour signifie succès et un exception signifie échec.

Je ne vois aucune raison dans les exemples que vous avez fournis de ne pas utiliser exceptions en cas d'échec.

L'utilisation d'une exception est une mauvaise idée dans certains cas (en particulier lors de l'écriture d'un serveur). Vous auriez besoin de deux types de méthode. Consultez également la classe des dictionnaires pour savoir ce que vous devriez faire.

// NB:  A bool is the return value. 
//      This makes it possible to put this beast in if statements.
public bool TryCalculatePoint(... out Point result) { }

public Point CalculatePoint(...)
{
   Point result;
   if(!TryCalculatePoint(... out result))
       throw new BogusPointException();
   return result;
}

Le meilleur des deux mondes!

La tentative TrySomething () est au moins une pratique qui fonctionne bien pour les méthodes d'analyse de .net, mais je ne pense pas que cela me plaise en général.

Lancer une exception est souvent une bonne chose, même si cela ne devrait pas être utilisé dans des situations normales, et cela a un coût de performance associé.

Retourner null lorsque c'est possible est dans la plupart des cas ok, lorsque vous ne voulez pas d'exception.

Cependant - votre approche est un peu procédurale - pourquoi ne pas créer quelque chose comme une classe PointCalculator - prenant les données requises en tant que paramètres dans le constructeur? Ensuite, vous appelez CalculatePoint et accédez au résultat par le biais de propriétés (propriétés distinctes pour Point et pour Success).

Vous ne voulez pas lancer d'exceptions quand quelque chose est prévu, car les exceptions @Kevin sont exceptionnelles.

Vous devriez retourner quelque chose qui est attendu pour "l'échec", généralement null est mon choix de retour incorrect.

La documentation de votre méthode doit informer les utilisateurs de ce à quoi s'attendre lorsque les données ne sont pas calculées .

Nous avons une fois écrit un Framework complet dans lequel toutes les méthodes publiques renvoyaient true (exécuté avec succès) ou false (une erreur s'était produite). Si nous devions renvoyer une valeur, nous utilisions des paramètres de sortie. Contrairement à la croyance populaire, cette façon de programmer a en réalité simplifié une grande partie de notre code.

Bien avec Point, vous pouvez renvoyer Point.Empty comme valeur de retour en cas d’échec. Maintenant tout ce que cela fait réellement est de retourner un point avec 0 pour les valeurs X et Y, donc si cela peut être une valeur de retour valide, je resterais à l'écart de cela, mais si votre méthode ne retournera jamais un point (0,0) , alors vous pouvez l'utiliser.

Désolé, je viens de me souvenir du type Nullable, vous devriez regarder ça. Je ne sais pas trop quels sont les frais généraux.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top