C# – Fonctions « semi-variadiques » et mot-clé params

Il est banal de faire une fonction calculant la somme de deux entiers :

static int Somme(int x, int y) => x + y;

Il est bien sûr aussi banal de faire la somme de trois entiers :

static int Somme(int e0, int e1, int e2) => e0 + e1 + e2;

... mais si nous souhaitons faire la somme d'un nombre arbitrairement grand d'entiers, cette mécanique d'écriture à la pièce de fonctions de signatures différentes devient rapidement fastidieuse. Pour faciliter ce type de calcul, C# propose le mot clé params, qui synthétise un tableau à partir des paramètres passés à une fonction :

static int Somme(params int [] elems)
{
   int cumul = 0;
   foreach(int e in elems) cumul += e;
   return cumul;
}
static void Main()
{
   Console.WriteLine(Somme(2,3,5,7,11)); // 28
}

Notez que dans une signature de fonction, si params apparaît, il doit n'apparaître qu'une seule fois, et ne peut s'appliquer qu'au dernier paramètre de la signature.

Ce mécanisme peut être utilisé à des fins génériques :

static T Cumuler<T,U>(Func<U,T,U> f, U init, params T [] elems)
{
   foreach(T e in elems) init = f(init, e);
   return init;
}
static bool RespectentTous<T>(Func<T,bool> pred, params T [] args)
{
   foreach(T arg in args)
      if(!pred(arg))
         return false;
   return true;
}
static void Main()
{
   Console.WriteLine(Cumuler((x, y) => x + y, 0, 2, 3, 5, 7, 11)); // 28
   if(!RespectentTous(n => n % 2 == 0, 0, 2, 4, 6, 8, 10))
   {
      Console.WriteLine("Suspect...");
   }
}

Puisque C# exprime les paramètres passés en tant que params sous forme d'un tableau, les éléments doivent tous être du même type (cela explique que Console.Write(), par exemple, accepte en paramètre un params object[] et utilise la méthode ToString() de chacun). Notez toutefois que C# réalise certaines conversions susceptibles d'être suprenantes en synthétisant un tableau :

static T CumulSimpliste<T>(Func<T,T,T> f, params T [] elems)
{
   T cumul = default;
   foreach(T e in elems) cumul = f(cumul, e);
   return cumul;
}
static void Main()
{
   Console.WriteLine(CumulSimpliste((x,y)=>x+y,2,3,5,7,11)); // 28, un int
   Console.WriteLine(CumulSimpliste((x,y)=>x+y,2,3.5,5,7,11)); // 28.5, un double
}

En effet, dans le deuxième appel de CumulSimpliste, C# voit quatre int et un double, et décide de les traiter comme cinq double du fait qu'il existe une conversion implicite de int vers double mais pas dans le sens inverse. C'est un choix discutable, mais c'est le choix de ce langage. Mieux vaut le savoir.

Lectures complémentaires

Quelques liens pour enrichir le propos.

(à venir)


Valid XHTML 1.0 Transitional

CSS Valide !