29 votes

Quelle est la syntaxe la plus fluide et la plus attrayante que vous ayez trouvée pour affirmer que les paramètres sont corrects en C# ?

Un problème courant dans n'importe quel langage est d'affirmer que les paramètres envoyés à une méthode répondent à vos exigences, et si ce n'est pas le cas, d'envoyer des messages d'erreur agréables et informatifs. Ce type de code est répété à l'infini, et nous essayons souvent de créer des aides à cet effet. Cependant, en C#, il semble que ces aides soient obligées de faire face à une certaine duplication qui nous est imposée par le langage et le compilateur. Pour montrer ce que je veux dire, laissez-moi vous présenter du code brut sans aide, suivi d'une aide possible. Ensuite, je soulignerai la duplication dans l'aide et formulerai ma question de manière précise.

Tout d'abord, le code sans aucune aide :

public void SomeMethod(string firstName, string lastName, int age)
{
     if(firstName == null)
     {
          throw new WhateverException("The value for firstName cannot be null.");
     }

     if(lastName == null)
     {
          throw new WhateverException("The value for lastName cannot be null.");
     }

     // Same kind of code for age, making sure it is a reasonable range (< 150, for example).
     // You get the idea
}

}

Maintenant, le code avec une tentative raisonnable d'aide :

public void SomeMethod(string firstName, string lastName, int age)
{
      Helper.Validate( x=> x !=null, "firstName", firstName);
      Helper.Validate( x=> x!= null, "lastName", lastName);
}

La question principale est la suivante : Remarquez comment le code doit passer la valeur du paramètre et le nom du paramètre ("firstName") et firstName). Ainsi, le message d'erreur peut dire "Bla bla bla, la valeur de l'attribut premierNom paramètre". Avez-vous trouvé un moyen de contourner ce problème en utilisant la réflexion ou autre chose ? Ou un moyen de le rendre moins pénible ?

Et plus généralement, avez-vous trouvé d'autres moyens de rationaliser cette tâche de validation des paramètres tout en réduisant la duplication du code ?

EDIT : J'ai lu des personnes parler de l'utilisation de la propriété Parameters, mais je n'ai jamais trouvé de moyen de contourner la duplication. Quelqu'un a eu de la chance avec ça ?

Merci !

15voto

John Feminella Points 116878

Vous devriez vérifier Contrats de code ils font à peu près exactement ce que vous demandez. Exemple :

[Pure]
public static double GetDistance(Point p1, Point p2)
{
    CodeContract.RequiresAlways(p1 != null);
    CodeContract.RequiresAlways(p2 != null); 
    // ...
}

11voto

Charlie Flowers Points 9145

Wow, j'ai trouvé quelque chose de vraiment intéressant ici. Chris ci-dessus a donné un lien vers une autre question de Stack Overflow. L'une des réponses à cette question renvoie à un article de blog qui décrit comment obtenir un code comme celui-ci :

public static void Copy<T>(T[] dst, long dstOffset, T[] src, long srcOffset, long length)
{
    Validate.Begin()
            .IsNotNull(dst, “dst”)
            .IsNotNull(src, “src”)
            .Check()
            .IsPositive(length)
            .IsIndexInRange(dst, dstOffset, “dstOffset”)
            .IsIndexInRange(dst, dstOffset + length, “dstOffset + length”)
            .IsIndexInRange(src, srcOffset, “srcOffset”)
            .IsIndexInRange(src, srcOffset + length, “srcOffset + length”)
            .Check();

    for (int di = dstOffset; di < dstOffset + length; ++di)
        dst[di] = src[di - dstOffset + srcOffset];
}

Je ne suis pas convaincu que ce soit le cas le site pas encore la meilleure réponse, mais c'est certainement intéressant. Voici l'article du blog de Rick Brewster.

7voto

Daniel Earwicker Points 63298

Je me suis attaqué à ce problème précis il y a quelques semaines, après avoir pensé qu'il était étrange que les bibliothèques de test semblent avoir besoin d'un million de versions différentes de Assert pour rendre leurs messages descriptifs.

Voici ma solution .

Bref résumé - étant donné ce bout de code :

int x = 3;
string t = "hi";
Assert(() => 5*x + (2 / t.Length) < 99);

Ma fonction Assert peut imprimer le résumé suivant de ce qui lui est passé :

(((5 * x) + (2 / t.Length)) < 99) == True where
{
  ((5 * x) + (2 / t.Length)) == 16 where
  {
    (5 * x) == 15 where
    {
      x == 3
    }
    (2 / t.Length) == 1 where
    {
      t.Length == 2 where
      {
        t == "hi"
      }
    }
  }
}

Ainsi, tous les noms et valeurs des identificateurs, ainsi que la structure de l'expression, pourraient être inclus dans le message d'exception, sans que vous ayez à les reformuler dans des chaînes entre guillemets.

6voto

Chris Points 8576

6voto

Charlie Flowers Points 9145

Bon les gars, c'est encore moi, et j'ai trouvé quelque chose d'autre qui est étonnant et délicieux. Il s'agit d'un autre article de blog auquel il est fait référence dans l'autre question sur les SO que Chris, ci-dessus, a mentionnée.

L'approche de ce type vous permet d'écrire ceci :

public class WebServer
    {
        public void BootstrapServer( int port, string rootDirectory, string serverName )
        {
            Guard.IsNotNull( () => rootDirectory );
            Guard.IsNotNull( () => serverName );

            // Bootstrap the server
        }
    }

Notez qu'il n'y a aucune chaîne contenant "rootDirectory" et aucune chaîne contenant "serverName" ! Et pourtant ses messages d'erreur peuvent dire quelque chose comme "Le paramètre rootDirectory ne doit pas être nul".

C'est exactement ce que je voulais et plus que ce que j'espérais. Voici le lien vers l'article du blog de ce type.

Et la mise en œuvre est assez simple, comme suit :

public static class Guard
    {
        public static void IsNotNull<T>(Expression<Func<T>> expr)
        {
            // expression value != default of T
            if (!expr.Compile()().Equals(default(T)))
                return;

            var param = (MemberExpression) expr.Body;
            throw new ArgumentNullException(param.Member.Name);
        }
    }

Notez que cette méthode fait appel à la "réflexion statique". Dans le cas d'une boucle serrée ou autre, il est préférable d'utiliser l'approche de Rick Brewster ci-dessus.

Dès que j'aurai posté ce message, je voterai pour Chris et pour la réponse à l'autre question de SO. C'est du bon travail ! !!

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