web-dev-qa-db-fra.com

Est-il préférable de renvoyer une tâche vide ou nulle? c #

J'ai une méthode asynchrone qui recherchera un jobId pour un service de planification de travaux via une API.

s'il ne trouve aucun résultat, vaut-il mieux renvoyer une tâche vide ou NULL?

Si je comprends bien lors du renvoi d'une collection, il est préférable de renvoyer une collection vide plutôt que null et d'utiliser des objets, il vaut mieux renvoyer null qu'un objet vide. mais avec des tâches, je ne sais pas quel est le meilleur. Voir la méthode ci-jointe.

Je vous remercie

   public virtual Task<int> GetJobRunIdAsync(int jobId)
        {
            var jobMonRequest = new jobmonRequest(true, true, true, true, true, 
            true, true, true, true, true, true, true,
            true,
            true, true, true, DateTime.Today, jobId, null, 0, null, null,
            null, null, 0, 0);

        var jobMonResponseTask = Client.jobmonAsync(jobMonRequest);

        var jobTask = jobMonResponseTask.ContinueWith(task =>
        {
            if (jobMonResponseTask.Result == null )
            {
                var empty = new Task<int>(() => 0); // as i understand creating a task with a predefined result will reduce overhead.

                return empty.Result;   // || is it better to just return null?
            }
            if (jobMonResponseTask.Result.jobrun.Length > 1)
            {
                throw  new Exception("More than one job found, Wizards are abound.");
            }
              return jobMonResponseTask.Result.jobrun.Single().id;
        });

        return jobTask;
    }
6
crayoyeah

s'il ne trouve aucun résultat, vaut-il mieux renvoyer une tâche vide ou NULL?

Il y a quelques points à considérer ici:

Tout d’abord, vous devriez ne jamais renvoyer null Task. Dans le monde async, une tâche null n'a tout simplement aucun sens. Task représente l'exécution de la méthode asynchrone. Ainsi, pour une méthode asynchrone, renvoyer une tâche null revient à dire au code d'appel "vous n'avez pas vraiment appelé cette méthode", bien sûr.

Ainsi, un Task/Task<T> renvoyé par une méthode ne devrait jamais, jamais être null. Cependant, vous avez toujours la possibilité de retourner une nullvaleur dans une tâche normale. Ça dépend de toi.

avec des tâches, je ne sais pas quel est le meilleur.

La tâche est juste un wrapper. La logique sous-jacente est toujours la même. Pensez à quoi cette méthode aurait l'air si elle était synchrone; votre type de retour serait-il int et retournera-t-il 0 si rien n'a été trouvé, ou votre type de retour sera-t-il int? et renverra-t-il null si rien n'a été trouvé? Après avoir fait ce choix pour une méthode synchrone, enveloppez-le dans Task<T> pour la méthode asynchrone.

Pour terminer, je dois dire:

Votre méthode peut être radicalement simplifiée:

public virtual async Task<int> GetJobRunIdAsync(int jobId)
{
  var jobMonRequest = ...;
  var jobMonResponse = await Client.jobmonAsync(jobMonRequest);
  if (jobMonResponse == null)
    return 0;
  if (jobMonResponse.jobrun.Length > 1)
    throw  new Exception("More than one job found, Wizards are abound.");
  return jobMonResponse.jobrun.Single().id;
}

Ou, si vous souhaitez renvoyer une valeur (pas une tâche) de null:

public virtual async Task<int?> GetJobRunIdAsync(int jobId)
{
  var jobMonRequest = ...;
  var jobMonResponse = await Client.jobmonAsync(jobMonRequest);
  if (jobMonResponse == null)
    return null;
  if (jobMonResponse.jobrun.Length > 1)
    throw  new Exception("More than one job found, Wizards are abound.");
  return jobMonResponse.jobrun.Single().id;
}
7
Stephen Cleary

Ma préférence personnelle est d'éviter null autant que possible. Cela oblige l'appelant à mettre en œuvre la vérification de la valeur de retour et réduit la variable NullReferenceException non intentionnelle.

La seule fois où j'utiliserais null serait pour un retour de type valeur. Les types de valeur Nullable fournissent les propriétés HasValue et Value afin que l'appelant puisse effectuer les tâches suivantes:

var jobId = api.GetJobRunIdAsync(1234).Result; //note: highly recommend using async/await here instead of just returning a task
if(jobId.HasValue)
{
   var result = DoSomethingWithId(jobId);
   //continue processing...
}

Je pense que cela fonctionnerait bien dans l'exemple que vous avez fourni, puisque vous retournez int.

Lors du retour d'une collection, je préférerais une collection vide à un objet null. Cela nécessite moins de branchements, ce qui facilite la lecture et le test du code - si une collection null est renvoyée, vous obtenez quelque chose comme:

var results = await GetResultsForJobId(1234);
if(results != null) {}
// or results?.SomeLinqOperation();

Avec une collection vide c'est tout simplement

var results = await GetResultsForJobId(1234);
results.SomeLinqOperation();

Pour les autres types de référence de collection, je suggérerais de mettre en œuvre un Maybe<T> ou un Optional<T>, qui peut être utilisé avec des types de référence de la même manière que Nullable<T>. Un exemple d'une telle implémentation peut être trouvé sur GitHub à https://github.com/nlkl/Optional . Une version plus simple pourrait être:

public struct Optional<T>
{
    private static readonly Optional<T> _readOnlyEmpty = new Optional<T>();
    public static Optional<T> Empty => _readOnlyEmpty;

    public T Value { get; }

    public bool HasValue { get; private set; }

    public Optional(T value)
        : this()
    {
        Value = value;
        HasValue = true;
    }

    public static implicit operator Optional<T>(T value)
    {
        return new Optional<T>(value);
    }

    public static implicit operator T(Optional<T> optional)
    {
        return optional.Value;
    }
}
0
E. Moffat

Il est préférable de renvoyer une collection vide plutôt que null car les collections implémenteront souvent IEnumerable et, en tant que telles, seront itérées via foreach(var item in collection).

Foreach rencontrera une NullReferenceException si l'objet de la collection est null au lieu d'une collection vide. Le renvoi d'une collection vide évitera simplement les plantages du programme dus à l'oubli d'une vérification de référence nulle dans ce cas et est tout simplement plus intuitif.

De plus, les objets d'une collection ne peuvent parfois être créés que lorsqu'ils sont nécessaires, par exemple, via l'instruction yield. Avant cela, le résultat n'existait tout simplement pas encore et donc null pourrait être problématique ici, donc retourner une collection vide au lieu de null a un sens ici.

Toutefois, si vous retournez un seul objet, renvoyer null est parfaitement valide s’il est possible que l’entité représentée par l’objet n’existe pas et puisse être différente d’un objet vide (dont les propriétés peuvent avoir pour propriétés des valeurs par défaut, etc.). En cas d'échec, vous devrez vérifier s'il échouait de toute façon, donc null dans ce cas-ci n'est pas un problème et en fait, le résultat souhaité s'il n'y a rien que la référence puisse représenter.

En ce qui concerne la Task. La Task elle-même est nécessaire pour vérifier ou attendre son achèvement. Si vous devez être sûr que la Task est terminée, retournez une Task vide. Si vous n'avez pas besoin de vérifier l'achèvement de Task et simplement de tirer et de l'oublier, alors quel est le but de réaccorder quoi que ce soit?

0
Adwaenyth

Il y a deux points ici. D'abord la valeur de retour où il n'y a pas de résultat valide. Je changerais le type de retour en int? et renvoie la valeur null pour l'id de jobRun pour indiquer à l'appelant qu'il n'y a pas de valeur valide.

L'autre partie de la procédure à suivre pour renvoyer une tâche est détaillée ici Si mon interface doit renvoyer une tâche, quel est le meilleur moyen d'avoir une implémentation sans opération?

0
Rahul Misra