En C #, avec .NET Framework 4, existe-t-il un moyen élégant de répéter la même action un nombre de fois déterminé? Par exemple, au lieu de:
int repeat = 10;
for (int i = 0; i < repeat; i++)
{
Console.WriteLine("Hello World.");
this.DoSomeStuff();
}
J'aimerais écrire quelque chose comme:
Action toRepeat = () =>
{
Console.WriteLine("Hello World.");
this.DoSomeStuff();
};
toRepeat.Repeat(10);
ou:
Enumerable.Repeat(10, () =>
{
Console.WriteLine("Hello World.");
this.DoSomeStuff();
});
Je sais que je peux créer ma propre méthode d’extension pour le premier exemple, mais n’existe-t-il pas une fonctionnalité permettant déjà de le faire?
Il n'y a pas de manière intégrée de faire cela.
La raison en est que C #, en tant que tel, essaie de créer une distinction entre les côtés fonctionnel et impératif du langage. C # ne facilite la programmation fonctionnelle que lorsqu'il ne produit pas d'effets secondaires. Ainsi, vous obtenez des méthodes de manipulation de collection telles que Where
, Select
, etc. de LINQ, mais vous n'obtenez pas ForEach
.1
De la même manière, vous essayez ici de trouver un moyen fonctionnel d’exprimer ce qui est essentiellement une action impérative. Bien que C # vous donne les outils pour le faire, il n'essaie pas de vous faciliter la tâche, car cela rend votre code flou et non idiomatique.
1 Il existe un List<T>.ForEach
, mais pas un IEnumerable<T>.ForEach
. Je dirais que l’existence de List<T>.ForEach
est un artefact historique, dû au fait que les concepteurs de l’infrastructure n’avaient pas réfléchi à ces problèmes à l’époque de .NET 2.0; la nécessité d'une division claire n'est apparue qu'en 3.0.
Comme ça?
using System.Linq;
Enumerable.Range(0, 10).ForEach(arg => toRepeat());
Cela exécutera votre méthode 10 fois.
[Modifier]
Je suis tellement habitué à avoir la méthode d'extension ForEach
sur Enumerable, que j'ai oublié qu'elle ne faisait pas partie de FCL.
public static void ForEach<T>(this IEnumerable<T> source, Action<T> action)
{
foreach (var item in source)
action(item);
}
Voici ce que vous pouvez faire sans la méthode d'extension ForEach
:
Enumerable.Range(0, 10).ToList().ForEach(arg => toRepeat());
[Modifier]
Je pense que la solution la plus élégante consiste à implémenter une méthode réutilisable:
public static void RepeatAction(int repeatCount, Action action)
{
for (int i = 0; i < repeatCount; i++)
action();
}
Usage:
RepeatAction(10, () => { Console.WriteLine("Hello World."); });
Pour la brièveté d'un one-line, vous pouvez le faire. Je ne sais pas ce que vous pensez ...
Enumerable.Repeat<Action>(() =>
{
Console.WriteLine("Hello World.");
this.DoSomeStuff();
}, 10).ToList().ForEach(x => x());
Sans déployer votre propre extension, je suppose que vous pouvez faire quelque chose comme ceci
Action toRepeat = () => {
Console.WriteLine("Hello World.");
this.DoSomeStuff();
};
int repeat = 10;
Enumerable.Range(0, repeat).ToList().ForEach(i => toRepeat());
Enumerable.Repeat<Action>(() => { Console.WriteLine("Hello World"); this.DoSomething(); },10).ToList().ForEach(f => f.Invoke());
élégant n'est ce pas?
Déclarer une extension:
public static void Repeat(this Action action, int times){
while (times-- > 0)
action.Invoke();
}
Vous pouvez utiliser la méthode d'extension en tant que:
new Action(() =>
{
Console.WriteLine("Hello World.");
this.DoSomeStuff();
}).Repeat(10);
Table table = frame.AddTable();
int columnsCount = 7;
Enumerable.Repeat<Func<Column>>(table.AddColumn, columnsCount)
.ToList()
.ForEach(addColumn => addColumn());
//or
Enumerable.Range(0, columnsCount)
.ToList()
.ForEach(iteration => table.AddColumn());
ces options ne sont pas élégantes à cause de ToList (), mais les deux ont fonctionné dans mon cas
Je l'ai écrit en tant que méthode d'extension Int32
. Au début, je pensais que cela ne semblait peut-être pas être une bonne idée, mais en réalité, cela semble vraiment génial quand vous l'utilisez.
public static void Repeat(
this int count,
Action action
) {
for (int x = 0; x < count; x += 1)
action();
}
Usage:
5.Repeat(DoThing);
value.Repeat(() => queue.Enqueue(someItem));
(value1 - value2).Repeat(() => {
// complex implementation
});
Notez que cela ne fera rien pour les valeurs <= 0
. Au début, j'allais lancer pour les négatifs, mais ensuite, je devais toujours vérifier quel nombre était le plus grand en comparant deux. Cela permet à une différence de fonctionner si elle est positive et de ne rien faire autrement.
Vous pouvez écrire une méthode d'extension Abs
(soit value.Abs().Repeat(() => { });
ou -5.RepeatAbs(() => { });
) si vous souhaitez répéter, quel que soit le signe et l'ordre dans lequel la différence de deux chiffres n'a plus d'importance.
D'autres variantes sont également possibles, telles que le passage de l'index ou la projection dans des valeurs similaires à Select
.
Je sais qu'il existe Enumerable.Range
mais c'est beaucoup plus concis.