Как «продолжить» цикл ForEach из вложенного метода? - PullRequest
12 голосов
/ 06 мая 2011

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

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

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

Ответы [ 8 ]

21 голосов
/ 06 мая 2011

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

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

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

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

6 голосов
/ 06 мая 2011

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

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

4 голосов
/ 06 мая 2011

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

2 голосов
/ 06 мая 2011

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

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

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

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

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

Примерно так:

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;
}
}
2 голосов
/ 06 мая 2011

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

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

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

Чтобы расширить ответ Erics, решение состоит в том, чтобы реорганизовать ваши циклы таким образом, чтобы внешний цикл имел больший контроль и влияние на долгосрочные методы, которые он вызывает.Кнопки «Отмена», которые позволяют пользователю либо пропустить контракт, либо полностью отменить обработку - вы можете написать свой код примерно так:

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 (например, на основе пользовательского ввода).

1 голос
/ 06 мая 2011

Одним из решений было бы создание пользовательского исключения, которое всплыло бы и, наконец, было бы перехвачено вашим 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);
}
0 голосов
/ 06 мая 2011

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

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

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


bool Continue(int k)
{
    return what's suitable
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...