web-dev-qa-db-ja.com

匿名デリゲートを使用しているときにList <string> .ForEachループを終了するにはどうすればよいですか?

通常のループでは、breakを使用してループから抜けることができます。匿名デリゲートを使用しても同じことができますか?

InputStringとresultの例は両方ともデリゲートの外部で宣言されています。

blackList.ForEach(new Action<string>(
    delegate(string item)
    {
        if(inputString.Contains(item)==true)
        {
            result = true;
            // I want to break here
        }
    }
));

編集:返信ありがとう、私は実際にジョンの瞬間にあなたの本を読んでいます:)記録のために、私はこの問題を見つけ、通常のforeachループに切り替えましたが、私は何かを逃したかどうかを確認するためにこの質問を投稿しました。

38
SecretDeveloper

他の人が投稿したように、ForEachでループを終了することはできません。

LINQを使用できますか?その場合、TakeWhileとカスタムのForEach拡張メソッドを簡単に組み合わせることができます(最近ではほとんどすべてのプロジェクトにあるようです)。

ただし、あなたの例では、 List<T>.FindIndex が最良の代替手段になりますが、実際にそれを行っていない場合は、reallyがやりたいことの例を投稿してください。

40
Jon Skeet

アクセスできるループはなく、そこから中断することもできません。 (匿名)デリゲートへの各呼び出しは新しい関数呼び出しであるため、ローカル変数は役に立ちません。しかし、C#はクロージャを提供するため、フラグを設定して、その後の呼び出しでは何も実行できません。

bool stop = false;
myList.ForEach((a) => {
  if (stop) {
    return;
  } else if (a.SomeCondition()) {
    stop = true;
  }
});

(これは、クロージャーの正しい参照セマンティクスが生成されるかどうかを確認するためにテストする必要があります。)

より高度なアプローチは、ループを停止するためにデリゲートがfalseを返すことを可能にする独自の拡張メソッドを作成することです。

static class MyExtensions {
  static void ForEachStoppable<T>(this IEnumerable<T> input, Func<T, bool> action) {
    foreach (T t in input) {
      if (!action(t)) {
        break;
      }
    }
  }
}
28
Richard

LINQを利用できますか?あなたのロジックはAnyに似ているようです:

bool any = blackList.Any(s=>inputString.Contains(s));

次と同じです:

bool any = blackList.Any(inputString.Contains);

LINQがない場合、これは次と同じです:

bool any = blackList.Find(inputString.Contains) != null;

追加のロジックを実行する場合は、TakeWhileなどを使用して(LINQを使用して)できることがあります。

15
Marc Gravell

ForEachメソッドを使用する場合、エレガントな方法はないと思います。ハックな解決策は、例外をスローすることです。

昔ながらのforeachをするのを妨げているのは何ですか?

foreach (string item in blackList)
{
    if (!inputString.Contains(item)) continue;

    result = true;
    break;
}
6
Michael Meadows

ループが必要な場合は、ループを使用します。

Actionは戻り値を許可しないため、例外をスローするまで、ForEach関数がブレークしたいことを知る方法はありません。ここで例外を使用すると過剰になります。

3
Yuliy

ループを「終了」する唯一の方法は、例外をスローすることです。通常のforeachループのように.ForEachメソッドを終了する「ブレーク」スタイルの方法はありません。

2
JaredPar

ForEachメソッドは、これを行うことを意味しません。コレクションにアイテムが含まれているかどうかを知りたい場合は、Containsメソッドを使用する必要があります。コレクション内のすべてのアイテムのチェックを実行する場合は、任意の拡張方法を試してください。

2
Rune Grimstad

リストにループforeachを本当に存在させたい場合は、次のコードのような例外を使用できます。

public class ExitMyForEachListException : Exception
{
    public ExitMyForEachListException(string message)
        : base(message)
    {
    }
}
class Program
{
    static void Main(string[] args)
    {
        List<string> str = new List<string>() { "Name1", "name2", "name3", "name4", "name5", "name6", "name7" };
        try
        {
            str.ForEach(z =>
            {
                if (z.EndsWith("6"))
                    throw new ExitMyForEachListException("I get Out because I found name number 6!");
                System.Console.WriteLine(z);
            });
        }
        catch (ExitMyForEachListException ex)
        {
            System.Console.WriteLine(ex.Message);
        }

        System.Console.Read();
    }
}

この助けが他の視点を得るのを願っています。

1
    class Program
{
    static void Main(string[] args)
    {
        List<string> blackList = new List<string>(new[] { "jaime", "jhon", "febres", "velez" });
        string inputString = "febres";
        bool result = false;
        blackList.ForEach((item) =>
                              {
                                  Console.WriteLine("Executing");
                                  if (inputString.Contains(item))
                                  {
                                      result = true;
                                      Console.WriteLine("Founded!");
                                  }
                              },
                          () => result);
        Console.WriteLine(result);
        Console.ReadLine();
    }


}
public static class MyExtensions
{
    public static void ForEach<T>(this IEnumerable<T> enumerable, Action<T> action, Func<bool> breakOn)
    {
        foreach (var item in enumerable)
        {
            action(item);
            if (breakOn())
            {
                break;
            }
        }
    }
}
1
Jaime Febres
bool @break = false;

blackList.ForEach(item =>
 {  
    if(!@break && inputString.Contains(item))
     { @break = true;
       result = true;
     }

    if (@break) return;
    /* ... */
 });

上記は各アイテムを繰り返し処理しますが、すぐに戻ることに注意してください。もちろん、この方法は通常のforeachほど良くないでしょう。

1
Mark Cidade

これはあなたのために働くでしょうか:

bool result = null != blackList.Find( item => inputString.Contains(item)) );
0