是否有一种优雅的方式来重复动作?

时间:2011-06-20 04:11:16

标签: c# .net linq .net-4.0

在C#中,使用.NET Framework 4,是否有一种优雅的方法可以重复执行相同的操作一定次数?例如,而不是:

int repeat = 10;
for (int i = 0; i < repeat; i++)
{
    Console.WriteLine("Hello World.");
    this.DoSomeStuff();
}

我想写一些像:

Action toRepeat = () =>
{
    Console.WriteLine("Hello World.");
    this.DoSomeStuff();
};

toRepeat.Repeat(10);

或:

Enumerable.Repeat(10, () =>
{
    Console.WriteLine("Hello World.");
    this.DoSomeStuff();
});

我知道我可以为第一个例子创建我自己的扩展方法,但是不存在已经可以做到这一点的现有功能吗?

10 个答案:

答案 0 :(得分:38)

喜欢这个吗?

using System.Linq;

Enumerable.Range(0, 10).ForEach(arg => toRepeat());

这将执行你的方法10次。

<强> [编辑]

我已经习惯在Enumerable上使用ForEach扩展方法了,我忘了它不是FCL的一部分。

public static void ForEach<T>(this IEnumerable<T> source, Action<T> action)
{
    foreach (var item in source)
        action(item);
}

如果没有ForEach扩展方法,您可以执行以下操作:

Enumerable.Range(0, 10).ToList().ForEach(arg => toRepeat());

<强> [编辑]

我认为最优雅的解决方案是实现可重用的方法:

public static void RepeatAction(int repeatCount, Action action)
{
    for (int i = 0; i < repeatCount; i++)
        action();
}

用法:

RepeatAction(10, () => { Console.WriteLine("Hello World."); });

答案 1 :(得分:23)

没有内置方法可以做到这一点。

原因在于C#试图在语言的功能和命令方面之间进行划分。 C#只有在不产生副作用时才能轻松进行函数式编程。因此,您可以获得LINQ的WhereSelect等集合处理方法,但you do not get ForEach 1

以类似的方式,你在这里尝试做的是找到一些表达本质上是命令行动的功能性方法。尽管C#为您提供了执行此操作的工具,但它并不会让您感觉轻松,因为这样做会使您的代码不清晰且不具有惯用性。

1 List<T>.ForEach,但不是IEnumerable<T>.ForEach。我会说List<T>.ForEach的存在是一个历史的神器,源于框架设计者在.NET 2.0时代没有考虑过这些问题;只有在3.0中才能明确划分明确的分工。

答案 2 :(得分:9)

为了简化一个班轮你可以做到这一点。不确定你的想法...

Enumerable.Repeat<Action>(() => 
{
    Console.WriteLine("Hello World.");
    this.DoSomeStuff();
}, 10).ToList().ForEach(x => x());

答案 3 :(得分:7)

如果不推出自己的扩展程序,我猜你可以做这样的事情

    Action toRepeat = () => {
        Console.WriteLine("Hello World.");
         this.DoSomeStuff();
    };

    int repeat = 10;
    Enumerable.Range(0, repeat).ToList().ForEach(i => toRepeat());

答案 4 :(得分:6)

Enumerable.Repeat<Action>(() => { Console.WriteLine("Hello World"); this.DoSomething(); },10).ToList().ForEach(f => f.Invoke());

优雅不是吗?

答案 5 :(得分:1)

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());

由于ToList(),这些选项并不优雅,但两者都适用于我的情况

答案 6 :(得分:1)

声明扩展名:

public static void Repeat(this Action action, int times){
    while (times-- > 0)
        action.Invoke();
}

您可以将扩展方法用作:

        new Action(() =>
                   {
                       Console.WriteLine("Hello World.");
                       this.DoSomeStuff();
                   }).Repeat(10);

答案 7 :(得分:0)

我将其编写为Int32扩展方法。起初,我认为这听起来似乎不是一个好主意,但是当您使用它时,它似乎真的很棒。

public static void Repeat(
   this int count,
   Action action
) {
   for (int x = 0; x < count; x += 1)
      action();
}

用法:

5.Repeat(DoThing);

value.Repeat(() => queue.Enqueue(someItem));

(value1 - value2).Repeat(() => {
   // complex implementation
});

请注意,它将对值<= 0不执行任何操作。起初,我打算抛出负数,但是当比较两个数时,我总是必须检查哪个数字更大。如果差异为正,则可以使差异起作用,否则不进行任何操作。

如果您想不考虑符号而重复,则可以编写一个Abs扩展方法(value.Abs().Repeat(() => { });-5.RepeatAbs(() => { });),然后两个数字之差的顺序无关紧要

其他变体也是可能的,例如传递索引或投影到类似于Select的值。

我知道这里有Enumerable.Range,但是更加简洁。

答案 8 :(得分:0)

我发现最方便的是:

Enumerable.Range(0, 10).Select(_ => action());

答案 9 :(得分:0)

如果您的操作是线程安全的,您可以使用

Parallel.For(0, 10, i => myAction(schema));