Как "продолжить" цикл ForEach из вложенного метода?

У меня есть цикл ForEach, который обрабатывает довольно большой список контактов. Вместо того, чтобы выполнять кучу обработки в этом основном цикле, я вызываю метод, который в свою очередь вызывает метод и т.д. Методы вызывают другие методы в других классах, возможно, в других пространствах имен.

Как я могу выйти из текущего метода, если обнаружено условие, и я хочу перейти к следующему контакту? Я использую метод на нескольких уровнях от основного цикла ForEach.

Как правило, вы можете использовать ключевое слово continue внутри объекта ForEach, чтобы перейти к следующему элементу коллекции. continue не является вариантом в этом случае. Когда я набираю continue, он получает подчеркивание красным цветом с комментарием "Unresolved Message".

Итак, что мне делать?

Ответ 1

возможно, у вас есть флаг... что-то вроде bool conditionDetected, и когда условие обнаружено, вы просто установите его значение true и if (conditionDetected) return; из методов, пока не дойдете до вершины, где вы if( conditionDetected) continue, чтобы следующий... тогда вы снова установите его на false и продолжаете... вы получаете ошибку, потому что вы находитесь внутри цикла foreach при переходе к другому методу

Ответ 2

Вы идете по плохому пути здесь; сделайте шаг назад и пересмотрите свой дизайн.

В общем, это действительно плохая идея иметь методы, которые пытаются влиять на поток управления их вызывающих. Метод - это слуга вызывающего, а не ведущего. Метод не определяет, что делает вызывающий абонент; что не его дело. Скорее, метод:

  • выполняет вычисление и возвращает результат, или
  • изменяет некоторое состояние, а затем
  • выдает исключение, если операция не может быть успешно выполнена

Существуют расширенные стили потока управления, в которых вызовы работают вместе с вызывающими, чтобы определить, что происходит дальше, например, продолжение стиля перехода. Но вы не должны туда идти. Их очень трудно понять.

Ответ 3

Ваши поддерживающие методы должны возвращать значение, которое проверяется в for-loop основного метода, а затем continue оттуда, если это указывает значение.

Использование исключений - еще одна альтернатива, но их обычно считают более медленными - я не уверен в особенности в С#. Они также обычно считаются плохими, когда используются таким образом. Исключения должны быть выбрасываться в исключительных ситуациях, а не как нормальная часть контроля потока. Есть, возможно, ситуации, когда это нормально, чтобы использовать их таким образом, например, веб-платформу Play!, но вы, вероятно, не в одном из них.

Ответ 4

Нет простого способа выполнить это. Единственное место, где вы можете принудительно выполнить следующую итерацию цикла foreach, находится непосредственно внутри тела. Поэтому для этого вы должны выйти из метода и вернуться в тело цикла foreach.

Есть несколько способов добиться этого.

  • Выбросьте исключение: Пожалуйста, не делайте этого. Исключения не должны использоваться в качестве механизма потока управления.
  • Выйдите из метода и всех методов между вами и циклом foreach с кодом возврата, который заставляет тело выполнить оператор continue

Ответ 5

Если я правильно понимаю вашу проблему, у вас есть цикл for так:

for(int i = 0; i < 100; i++)
{
  DoComplexProcessing();
}

DoComplexProcessing затем вызывает другой метод, который вызывает другой метод и т.д.

Как только вы опуститесь, скажем, на 4 уровня, вы обнаружите условие (что бы оно ни было) и хотите прервать эту итерацию вашего DoComplexProcessing.

Предполагая, что это правильно, я должен иметь объект, который проходит вместе с цепочкой методов как параметр out. На каждом уровне, как только будет найдено "плохое" условие, я верну null (или некоторые другие значения по умолчанию значение, когда null не является опцией) и установите ссылочный объект в состояние, которое означает "прервать" . Каждый метод будет проверять это состояние "прервать" , а затем выполнить тот же "возврат null", установить объект на "прервать" вызов.

Что-то вроде этого:

TracerObject tracer = new tracer("good");
for(int i = 0; i < 100; i++)
{
  DoComplexProcessing(out tracer)
  if(tracer.status == "abort") DoSomethingElse()
}

следующий способ может сделать это

DoComplexProcessing(out TracerObject tracer)
{
   var myObject = new MyObject()
   myObject.Property = DoSlightlyLessComplexProcessing(myObject, out tracer)
   if(tracer.Status == "abort")
   {
     //set myObject.Property to some default value
   }
   return myObject;
}
}

Ответ 6

Одним из решений было бы выбросить настраиваемое исключение, которое бы пузырилось и, наконец, попало в ваш цикл foreach.

Убедитесь, что вы используете настраиваемое исключение (т.е. имеете его собственный тип, а не только с помощью инструкции catch(Exception)), таким образом вы знаете, что вы определенно ловите правильный.

В блоке catch просто продолжайте вместе с вашим циклом foreach (или соответствующим дескриптором).

try {
    MethodWithIteration(i);
} catch (ProcessingFailedException) {
    continue;
}

Если сбой по какой-либо конкретной причине, вы лучше всего именовали исключение, так что у вас нет типа исключения только для управления потоком вашего приложения, и это действительно имеет смысл. В будущем вы, возможно, захотите справиться с этим.


foreach(DoSomethingWithMe doSomething in objList)
{
    // Option 1 : Custom Processing Exception
    try
    {
        ProcessWithException(doSomething);
    } catch(ProcessingFailedException)
    {
        // Handle appropriately and continue
        // .. do something ..
        continue;
    }

    // Option 2 : Check return value of processing
    if (!ProcessWithBool(doSomething))
        continue;

    // Option 3 : Simply continue on like nothing happened
    // This only works if your function is the only one called (may not work with deeply-nested methods)
    ProcessWithReturn(doSomething);
}

Ответ 7

Чтобы расширить ответ на Эрикс, решение состоит в том, чтобы реорганизовать ваши циклы, чтобы внешний цикл имел больше контроля и влияния на длительные методы, которые он вызывает.

Например, предположим, что у вас есть кнопки "пропустить" и "отменить", которые позволяют пользователю либо пропустить контракт, либо полностью отменить обработку - вы можете немного изменить код:

foreach (var contact in contacts)
{
    if (Cancel)
    {
        return;
    }
    ContactProcessor processor = new ContactProcessor(contact);
    processor.Process(contact);
}

class ContactProcessor
{
    public bool Skip { get; set; }

    private readonly Contact contact;
    public ContactProcessor(Contact contact)
    {
        this.contact = contact;
    }

    public void Process()
    {
        if (!this.Skip)
        {
            FooSomething();
        }
        if (!this.Skip)
        {
            BarSomething();
        }
        // Etc...
    }

    publiv void BarSomething()
    {
        // Stuff goes here
        if (this.contact.WTF())
        {
            this.Skip = true;
        }
    }
}

(Очевидно, что здесь должно быть сделано много фишинга)

Идея состоит в том, что если управление обработкой важно для вас, тогда методы и классы, ответственные за эту обработку, должны иметь механизмы для управления, "испеченные". Имея целый класс, ответственный за обработку, если часто это хороший способ инкапсулировать это, он также делает очень легко делать такие вещи, как отчет о ходе выполнения.

Приведенный выше метод позволяет определить любой метод ContactProcessor, если он должен пропустить обработку (без исключений!) и установить флаг Skip. Он также потенциально позволяет внешнему циклу установить флаг Skip (например, на основе пользовательского ввода).

Ответ 8

чтобы использовать продолжение, вам нужно быть непосредственно внутри цикла, поэтому вам нужно знать о разрыве цикла, но не могли бы вы просто вернуть bool из методов?

int[] ints = {1,2,3,4,5,6};

foreach (var k in ints)
{
  if (Continue(k))
  {
       continue;
  }
}


bool Continue(int k)
{
    return what suitable
}