web-dev-qa-db-ja.com

アクションを繰り返すエレガントな方法はありますか?

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

最初の例で独自の拡張メソッドを作成できることは知っていますが、これを可能にする既存の機能はありませんか?

38

これを行う組み込みの方法はありません。

その理由は、C#がそのまま言語の機能面と命令面の分割を強制しようとするためです。 C#では、副作用が発生しない場合にのみ、関数型プログラミングを簡単に実行できます。したがって、LINQのWhereSelectなどのコレクション操作メソッドを取得できますが、 ForEach は取得できません。1

同様に、ここでやろうとしていることは、本質的に命令型のアクションであるものを表現する機能的な方法を見つけることです。 C#はこれを行うためのツールを提供しますが、簡単にしようとはしません。そうすると、コードが不明瞭で非正統的になるからです。

1 List<T>.ForEachはありますが、IEnumerable<T>.ForEachはありません。 List<T>.ForEachの存在は、フレームワーク設計者が.NET 2.0の頃にこれらの問題を熟考しなかったことに起因する歴史的な成果物です。明確な分割の必要性は3.0でのみ明らかになりました。

23
Domenic

このような?

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."); });
43
Alex Aza

ワンライナーを簡潔にするために、これを行うことができます。あなたがどう思うかわからない...

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

独自の拡張機能を展開することなく、このようなことができると思います

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

    int repeat = 10;
    Enumerable.Range(0, repeat).ToList().ForEach(i => toRepeat());
7
Bala R
Enumerable.Repeat<Action>(() => { Console.WriteLine("Hello World"); this.DoSomething(); },10).ToList().ForEach(f => f.Invoke());

エレガントではないですか?

6
Ali Humayun

拡張機能を宣言します。

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

拡張メソッドは次のように使用できます。

        new Action(() =>
                   {
                       Console.WriteLine("Hello World.");
                       this.DoSomeStuff();
                   }).Repeat(10);
1
Toni Rossmann
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()のためエレガントではありませんが、私の場合は両方とも機能しました

1
Oleg

_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_に対しては何もしないことに注意してください。最初はネガを投げるつもりでしたが、2つを比較するときは、どちらの数字が大きいかを常に確認する必要がありました。これにより、正の場合は差異が機能し、それ以外の場合は何も実行されません。

符号に関係なく繰り返したい場合は、Abs拡張メソッド(value.Abs().Repeat(() => { });または-5.RepeatAbs(() => { });のいずれか)を書くことができます。問題ない。

インデックスを渡す、またはSelectに似た値に投影するなど、他のバリアントも可能です。

_Enumerable.Range_があることは知っていますが、これはもっと簡潔です。

0
Dave Cousineau