ce qui est mieux, en utilisant un paramètre nullable ou boolean return + out

StackOverflow https://stackoverflow.com/questions/1800003

  •  05-07-2019
  •  | 
  •  

Question

Disons que j'ai une fonction qui doit renvoyer une valeur entière. mais il peut aussi échouer et j'ai besoin de savoir quand.

Quel est le meilleur moyen?

public int? DoSomethingWonderful()

ou

public bool DoSomethingWonderful(out int parameter)

C’est probablement plus une question de style, mais je suis toujours curieux de savoir quelle option les gens choisiraient.

Édition: clarification, ce code s’adresse à une boîte noire (appelons-le un nuage. non, une boîte noire. non, attendez. nuage, oui). Je me fiche de pourquoi cela a échoué. J'aurais juste besoin de savoir si j'ai une valeur valide ou non.

Était-ce utile?

La solution

J'aime mieux la version nullable, car vous pouvez utiliser l'opérateur null coalesce ?? dessus, par exemple:

int reallyTerrible = 0;
var mightBeWonderful = DoSomethingWonderful() ?? reallyTerrible;

Autres conseils

Cela dépend de l'apparence que devrait avoir le code d'appel. et, par conséquent, de l'utilisation de votre fonction.

En général, vous devriez éviter les arguments. Par contre, il pourrait être intéressant d’avoir un code comme celui-ci:

int parameter;
if (DoSomething(out paramameter))
{
  // use parameter
}

Lorsque vous avez un int nullable, cela ressemblerait à ceci:

int? result = DoSomething();
if (result != null)
{
  // use result
}

C’est un peu mieux car vous n’avez pas d’argument out, mais le code qui décide si la fonction a réussi n’a pas l’air très évident.

N'oubliez pas qu'il existe une autre option: utilisez Exeptions. Ne le faites que si le cas où votre fonction échoue est vraiment un cas exceptionnel et une sorte de cas d’erreur.

try
{
  // normal case
  int result = DoSomething()
}
catch (SomethingFailedException ex)
{
  // exceptional case
}

L'un des avantages de l'exception est que vous ne pouvez pas simplement l'ignorer. Le cas normal est également simple à mettre en œuvre. Si vous pouvez ignorer un cas exceptionnel, vous ne devriez pas utiliser d'exceptions.

Modifier: oublié de mentionner: un autre avantage d'une exception est que vous pouvez également fournir des informations pourquoi l'opération a échoué. Ces informations sont fournies par le type d’exception, les propriétés de l’exception et le texte du message.

Pourquoi ne pas lancer une exception?

Je suivrais le modèle utilisé quelque part dans la bibliothèque .Net comme:

bool int.TryParse(string s, out value)
bool Dictionary.TryGetValue(T1 key, out T2 value)

Je dirais donc:

public bool TryDoSomethingWonderful(out int parameter)

Cela dépend vraiment de ce que vous faites.

Est-ce que null est une réponse significative? Sinon, je préférerais un appel à la méthode bool TryDoSomethingWonderful (out int) . Cela correspond au Framework.

Si, cependant, null est une valeur de retour significative, return int? est logique.

À moins que les performances ne soient la préoccupation principale, vous devez renvoyer un int et émettre une exception en cas d'échec.

J'utiliserais le second, car j'ai probablement besoin de savoir tout de suite si l'appel a réussi. Dans ce cas, je préférerais écrire

.
int x;
if( DoSomethingWonderful( out x ) )
{
    SomethingElse(x);
}

que

int? x = DoSomethingWonderful();
if( x.HasValue )
{
   SomethingElse(x.Value);
}

Je suis en faveur de l’utilisation d’un paramètre de sortie. À mon avis, c’est le genre de situation pour laquelle l’utilisation d’un paramètre de sortie est la mieux adaptée.

Oui, vous pouvez utiliser l’opérateur de fusion pour conserver votre code comme une ligne si, et seulement si, vous disposez d’une valeur alternative que vous pouvez utiliser dans le reste de votre code. Je trouve souvent que ce n'est pas le cas pour moi et je préférerais exécuter un chemin de code différent de celui que je ferais si je pouvais récupérer une valeur.

        int value;
        if(DoSomethingWonderful(out value))
        {
            // continue on your merry way
        }
        else
        {
            // oops
            Log("Unable to do something wonderful");

            if (DoSomethingTerrible(out value))
            {
                // continue on your not-so-merry way
            }
            else
            {
                GiveUp();
            }
        }

De plus, si la valeur que je veux récupérer est réellement nullable, alors utiliser une fonction avec un paramètre de sortie et une valeur de retour booléenne est, à mon avis, le moyen le plus simple de faire la différence entre "J'ai échoué récupération de la valeur " et "La valeur que j'ai récupérée est nulle". Parfois, je tiens à cette distinction, comme dans l'exemple suivant:

    private int? _Value;
    private bool _ValueCanBeUsed = false;

    public int? Value
    {
        get { return this._Value; }
        set
        {
            this._Value = value;
            this._ValueCanBeUsed = true;
        }
    }

    public bool DoSomethingTerrible(out int? value)
    {
        if (this._ValueCanBeUsed)
        {
            value = this._Value;
            // prevent others from using this value until it has been set again
            this._ValueCanBeUsed = false;
            return true;
        }
        else
        {
            value = null;
            return false;
        }
    }

À mon avis, si la plupart des gens ont tendance à ne pas utiliser les paramètres de sortie, c'est parce qu'ils trouvent la syntaxe fastidieuse. Cependant, j’estime vraiment que l’utilisation des paramètres de sortie est la solution la plus appropriée pour résoudre ce problème, et j’ai trouvé qu’une fois que j’y suis habitué, j’ai trouvé la syntaxe bien préférable au retour d’une valeur nulle.

Si elle ne peut échouer que d'une seule façon, ou si vous n'avez jamais besoin de savoir pourquoi elle a échoué, je dirais qu'il est probablement plus simple et plus simple d'utiliser la valeur de retour nullable.

Inversement, s'il peut échouer de plusieurs manières et si le code appelant veut savoir exactement pourquoi, il utilise le paramètre out et renvoie un code d'erreur au lieu d'un bool (sinon, vous pouvez générer une exception. , mais sur la base de votre question, il semble que vous ayez déjà décidé de ne pas lancer d’exception).

Vous devriez plutôt utiliser une capture d’essai. Cela ressemble à l'appelant ne sait pas ce qui pourrait se passer?

Devrions-nous vérifier les valeurs booléenne et sortante, ou devrais-je vérifier les déclarations nulles et les déclarations effectives?

Laissez la méthode faire ce qu’elle devrait, et dans le cas où elle échouerait, informez l’appelant que cette dernière a échoué et appelez-la à la demande.

Chose intéressante, mon opinion personnelle dépend beaucoup de la nature de la méthode. Plus précisément, si le but de la méthode est de récupérer une valeur unique , par opposition à "faire quelque chose".

Ex:

bool GetSerialNumber(out string serialNumber)

vs

string GetSerialNumber() // returns null on failure

Le second se sent plus "naturel" pour moi en quelque sorte, et de même:

bool GetDeviceId(out int id)

vs

int? GetDeviceId() // returns null on failure`

Mais j’admets que cela tombe vraiment dans le "style de codage" territoire.

Oh, et moi aussi, nous aurions tendance à préférer le lancer d'exception:

int GetDeviceId() // throws an exception on read failure

Je ne comprends toujours pas pourquoi ils se trompent tellement. Peut-on avoir un fil dessus, Oren? ; -)

Je n'aime pas les "essais" de Microsoft. motif dans lequel le "out" " paramètre est utilisé pour renvoyer un élément de données. Entre autres choses, les méthodes codées de cette manière ne peuvent pas être utilisées dans des interfaces covariantes. Je préférerais voir une méthode codée comme suit: T GetValue (succès booléen) ou peut-être T GetValue (résultat GetValueErrorEnum); ou T GetValue (résultat GetValueErrorInfo ); si vous avez besoin de plus que vrai / faux. Étant donné que chaque type de données a une valeur par défaut légale, il n'y a aucun problème à décider quoi renvoyer si la fonction échoue. Le code appelant peut facilement dire:

  bool success;
  var myValue = Thing.GetValue(ref success);
  if (success)
    .. do something using myValue
  else
    .. ignore myValue

Ce serait bien si .net et C # offraient de véritables paramètres de "copie sortante" covariants (l'appelant allouait de l'espace pour le résultat, passait un pointeur sur cet espace vers la fonction appelée, puis copiait l'espace alloué sur la valeur passée. -in variable qu'après le retour de la fonction).

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