2

У меня есть следующий фрагмент кода, который дает мне проблемы, и я был бы признателен за любую помощь:Ошибки с Foreach цикл по списку

private static string CreateOptionString(List<VehicleOption> Options) 
{ 
    StringBuilder returnValue = new StringBuilder(); 
    foreach (VehicleOption option in Options) 
    { 
     if (option.OptionStatus == ExtendedResponse.OptionState.Included) 
     { 
      if (returnValue.Length > 0) 
      { 
       returnValue.Append(", "); 
      } 
      returnValue.Append(option.OptionName); 
     } 
    } 
    return returnValue.ToString(); 
} 

Моя первоначальная проблема заключалась в том, что я получал System.InvalidOperationException: сбор была изменена ошибка в моем цикле foreach.

1) Я все еще не могу понять, почему я получил бы эту ошибку, потому что я не вижу, чтобы она была изменена.

Кто-то предложил мне скопировать список в новый список и пропустить новый список. Я сделал это и избавился от InvalidOperationException. Тем не менее, я попытался переписать список 2 разными способами, и оба дали мне System.ArgumentException: массив назначения был недостаточно длинным. Вот два пути я пытался скопировать список

List<VehicleOption> newOptions = new List<VehicleOption>(Options); 

и

List<VehicleOption> newOptions = new List<VehicleOption>(); 
newOptions.AddRange(Options); 

Оба эти дали мне System.ArgumentException: массив назначения не было достаточно долго.

2) Почему любой из этих методов дал мне это исключение?

Любая помощь будет оценена, потому что я в тупике.

Спасибо!

+0

Как называется CreateOptionString? Вы уверены, что ничто не изменяет List вне этого метода? Вы также можете сделать List newList = новый список (Options.ToArray()); чтобы попытаться скопировать элементы в новый список. – dash

+1

Возможно, «Опции» модифицированы из другого потока во время 'foreach'. –

+0

В каком контексте это? ASP.NET? WCF? WinForms? –

ответ

4

Похоже, что объект списка, на который ссылается Options, меняется на другой поток. Это объясняет обе проблемы.

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

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

+0

Я не понимаю, как коллекция будет изменена через другой поток. Нет, где в методе эта коллекция будет изменена. – jkruer01

+0

Не в этом методе, но ваше приложение многопоточное? Может ли другой поток изменять этот объект списка одновременно? – cdhowie

5

Убедитесь, что никакие другие нити не меняют коллекцию во время ее повторения.

Кроме того, еще один способ сделать это будет:

return string.Join(", ", options.Where(
    op => op.OptionStatus == ExtendedResponse.OptionState.Included)); 

Который является лучше, и (удивительно) быстрее, чем при использовании StringBuilder в этом случае.

Тем не менее, это не решит вашу проблему - это, вероятно, вызвано другой нитью, изменяющей коллекцию.

Моей первая попытка будет:

private static string CreateOptionString(List<VehicleOption> Options) 
{ 
    lock (Options) 
    { 
     return string.Join(", ", options.Where(
      op => op.OptionStatus == ExtendedResponse.OptionState.Included)); 
    } 
} 

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

+0

+0. Хотя это полезное предложение вряд ли поможет в этом случае, так как есть итерация по элементам, которые не работают для OP. –

+0

Я согласен, но я также упомянул проблему с потоками, что наиболее вероятно, что вызывает проблему. Это похоже на ответ + предложение. Тем не менее, я отредактировал, чтобы ответить на этот вопрос. – SimpleVar

+0

+1 теперь после редактирования (также 'lock (Options)' не является обычным - лучше заблокировать какой-то более жестко контролируемый объект и должен быть выполнен и в другом потоке). –

0

Хорошо, я решил решить проблему. Я поместил блокировку (в список опций) вокруг вызова метода. Это устранило все мои проблемы.

Ценит всех!

Смежные вопросы