2017-09-22 7 views
0

J'ai deux questions et je voudrais de l'aide, s'il vous plaît.Nullable vs Out Paramètre lors de la vérification des valeurs valides/existantes

J'ai un code client qui doit accéder à une variable/valeur qui change dans le temps, en fait, il est calculé lors de la récupération, et il est récupéré par plusieurs méthodes plusieurs fois sur l'exécution, mais son calcul n'est pas toujours possible car les exigences pour cela ne sont pas toujours présentes, dans ce cas, un faux ou nul est retourné et le client vérifie cela pour décider de continuer. Maintenant, j'ai deux approches, la première, A, est mon classique, B cependant, me semble bien aussi.

A) J'ai cette méthode avec un paramètre de manière similaire aux méthodes TryParse sur certaines bibliothèques C#:

public bool GetLeadingTrailSegment(out Vector3 lastTrailSegment) 
{ 
    if (_line.positionCount > 1) 
    { 
     lastTrailSegment = lead - _line.GetPosition(_line.positionCount - 2); 
     return true; 
    } 
    lastTrailSegment = Vector3.zero; 
    return false; 
} 

B) J'ai cette propriété annulable qui essaie de faire le même travail que le code ci-dessus:

public Vector3? leadingTrailSegment 
{ 
    get 
    { 
     if (_line.positionCount > 1) 
     { 
      return lead - _line.GetPosition(_line.positionCount - 2); 
     } 
     return null; 
    } 
} 

le code client est la suivante:

A) Ici, le bool indique au code client si la valeur est sûre (utile?) À utiliser.

public bool IsDrawingOverAllowed(LayoutPointer pointer) 
{ 
    Vector3 leadingTrailSegment; 
    if (pointer.GetLeadingTrailSegment(out leadingTrailSegment)) 
    { 
     return !midline.ParallelTo(leadingTrailSegment); 
    } 
    return true; 
} 

B) Ici, le fait de la propriété HasValue du annulable étant faux indique au client wether il est sûr:

public bool IsDrawingOverAllowed(LayoutPointer pointer) 
{ 
    Vector3? leadingTrailSegment = pointer.leadingTrailSegment; 
    if (leadingTrailSegment.HasValue) 
    { 
     return !midline.ParallelTo(leadingTrailSegment.Value); 
    } 
    return true; 
} 

Première question: De ces deux approches, lequel est le meilleur ou quels sont les avantages/inconvénients entre ou défauts en leur sein?

Deuxième question: Je l'habitude d'avoir l'approche client B écrit:

public bool IsDrawingOverAllowed(LayoutPointer pointer) 
{ 
    if (pointer.leadingTrailSegment.HasValue) 
    { 
     return !midline.ParallelTo(pointer.leadingTrailSegment.Value); 
    } 
    return true; 
} 

C'était mal, non? Parce que la propriété Value du nullable peut avoir changé par le deuxième appel. J'aime mieux l'approche des paramètres out, vous pouvez utiliser le résultat dans la clause if, et la variable peut même être déclarée inline dans d'autres versions de C# mais j'aimerais vraiment donner une chance aux nullables et les rendre utile dans des situations comme celles-ci (et pas seulement lorsque je cherche une valeur non assignée, quels sont les cas dans lesquels je les utilise). J'espère que quelqu'un pourra donner son avis à ce sujet.

Merci.

+0

Pour la deuxième question, ajoutez un 'Vector3? segment = pointer.leadingTrailSegment; 'puis remplacez vos deux autres lignes par' if (segment.HasValue) 'et' return! midline.ParallelTo (segment.Value); 'une fois que vous avez copié le' Vector3? 'localement, sa valeur ne peut pas - changement plus long en raison de sa structure immuable. –

+0

Donc, mes devinettes étaient correctes alors? Je l'ai fait comme il est écrit dans l'approche B du client un exemple ci-dessus. Merci. – Ruri

+0

Oui, B dans la première question serait la meilleure façon de le faire. En outre, vos balises sont erronées. 'C# -4.0' n'est pas .NET 4.0. Unity3d à partir de 2017.1.0 Supporte C# 6 –

Répondre

4

Je préférerais de loin qu'un appel renvoie un zéro que d'utiliser des paramètres de sortie. Les paramètres de sortie sont une sorte de constructions de code sujettes "à effets secondaires" que personnellement je n'aime vraiment pas. Cela signifie que le code appelant doit définir une variable avant de l'utiliser, et il introduit des points faibles où il serait facile d'induire un bogue si vous introduisiez accidentellement la mauvaise variable dans l'appel. Cela vous empêche également d'utiliser le code dans une chaîne d'appel avec les opérateurs null-conditionnel et null-coalescent. Vous ne pouvez pas faire quelque chose comme var v = GetLeadingTrailSegment() ?? new Vector3();.

Le deuxième point d'intérêt est l'utilisation d'un Nullable. Si le type Vector3 est un type de valeur, alors c'est bien et cela a du sens. S'il s'agit d'un type de référence (à peu près tout autre que les types et les structures intégrales dans .NET), il n'est pas nécessaire. Juste return null; et if (variable != null) { ... }. L'argument en faveur du renvoi d'un bool est généralement lorsque vous avez des conflits de valeur de retour. Par exemple, si null a été retourné comme une valeur valide en soi, et vous avez besoin d'un moyen de différencier entre une valeur nulle valide ou une réponse invalide. Cela ne semble pas être le cas ici.

+0

Je suis d'accord sur les effets secondaires de l'utilisation de 'out' mais dans certains cas, je l'ai trouvé très pratique. 'Physics.Raycast()' est un. Certaines des surcharges n'ont * pas * de paramètre "out", par ex. ['if (Physics.Raycast (transform.position, fwd, 10)) {...}'] (https://docs.unity3d.com/ScriptReference/Physics.Raycast.html) et renvoyant * null * ici wouldn ne fait même pas * sens * (c'est pourquoi le retour est un booléen et l'objet 'out' est un effet secondaire) – Draco18s

0

Mes deux cents :)

tldr:

Je préférerais demander pourquoi vous voulez avoir une méthode qui retourne un booléen, mais son nom l'indique une autre chose.


Si j'ai player.GetCurrentHp() et la méthode retourne false si le joueur n'a pas de hp ou hp == 0, je tomberais que le nom est trompeur et je préférerais avoir une méthode player.isAlive().

Il n'y a pas de problème en soi d'un point de vue logique ou logiciel, mais je ne vais pas aider le prochain développeur à travailler avec ce code, ou vous-même dans 6 mois.

Dans votre cas, j'irais avec deux méthodes pour LayoutPointer;

public bool IsValid() // <--- I like when boolean methods represent 'is, can, have' actions, ideas, or properties. 
{ 
    return _line.positionCount > 1; 
} 

et

public bool GetLeadingTrailSegment() 
{ 
    if (!IsValid()) 
    { 
     return Vector3.zero; 
    } 

    return (lead - _line.GetPosition(_line.positionCount - 2)); 
} 

Et puis;

public bool IsDrawingOverAllowed(LayoutPointer pointer) 
{ 
    if (pointer == null) 
    { 
     Debug.LogWarning("IsDrawingOverAllowed: Pointer is null!"); 
     return true; // or false, it depends on your design.. 
    } 

    if (!pointer.IsValid()) // <-- I also like early returns :D 
    { 
     return true; 
    } 

    var leadingTrailSegment = pointer.GetLeadingTrailSegment() 
    return !midline.IsParallelTo(leadingTrailSegment); 
} 

Je sais qui peut être plus « bavard », mais rappelez-vous l'idée que créer du code pour les machines est facile, mais le code pour l'homme est plus difficile .. A la fin vous voulez avoir un code facile à lire, comprendre et maintenir.

Côté Remarque; Oui, je sais que parfois peut être utile, comme dans Physics.Raycast mais si vous ne mettez pas en œuvre le TryParse pattern (si vous voulez par exemple éviter l'utilisation de try/catch) je ne vois pas beaucoup de gain en essayant d'avoir une seule méthode qui fait deux choses .