有没有一种优雅的方式来重复一个动作
本文关键字:一个 一种 有没有 方式 | 更新日期: 2023-09-27 17:59:42
在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();
});
我知道我可以为第一个示例创建自己的扩展方法,但是没有现有的功能可以做到这一点吗?
像这样?
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."); });
没有内置的方法可以做到这一点。
原因是 C# 试图在语言的功能方面和命令性方面之间强制划分。C# 只有在不会产生副作用的情况下才能使函数式编程变得容易。因此,您可以获得诸如LINQ的Where
,Select
等集合操作方法,但是您不会得到ForEach
。1
以类似的方式,你在这里试图做的是找到一些功能性的方式来表达本质上是命令式操作的内容。尽管 C# 为您提供了执行此操作的工具,但它并没有尝试让您轻松,因为这样做会使您的代码不清楚且不惯用。
1 有List<T>.ForEach
,但没有IEnumerable<T>.ForEach
。我想说List<T>.ForEach
的存在是一个历史产物,源于框架设计者在 .NET 2.0 时代没有考虑这些问题;明确划分的必要性仅在3.0中才变得明显。
为了简洁起见,您可以这样做。不知道你怎么想...
Enumerable.Repeat<Action>(() =>
{
Console.WriteLine("Hello World.");
this.DoSomeStuff();
}, 10).ToList().ForEach(x => x());
无需推出自己的扩展,我想您可以做这样的事情
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());
优雅不是吗?
我发现最方便的是:
Enumerable.Range(0, 10).Select(_ => action());
我把它写成一个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
但这要简洁得多。
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((,这些选项并不优雅,但在我的情况下两者都有效
声明一个扩展:
public static void Repeat(this Action action, int times){
while (times-- > 0)
action.Invoke();
}
可以将扩展方法用作:
new Action(() =>
{
Console.WriteLine("Hello World.");
this.DoSomeStuff();
}).Repeat(10);
如果您的操作是线程安全的,则可以使用
Parallel.For(0, 10, i => myAction(schema));
这重复了两次,为了方便起见,还有一个迭代变量:
foreach (var iteration in Enumerable.Range(0, 2))
{
// do stuff
}