List<int> list = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
List<int> toRemove = new List<int>() { 2, 4, 6, 8 };
foreach(int item in list)
{
if(toRemove.Contains(item))
{
list.Remove(item);
}
}
Of course when this code runs, you immediately get the "Collection was modified; enumeration may not execute." error. Easily fixed by looping over a copy of the original list:
List<int> list = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
List<int> toRemove = new List<int>() { 2, 4, 6, 8 };
List<int> listCopy = new List<int>(list);
foreach(int item in listCopy)
{
if(toRemove.Contains(item))
{
list.Remove(item);
}
}
So, I created my own "loop-safe" list class, called LoopList
/// <summary>
/// List that allows modification inside a foreach loop.
/// </summary>
/// <typeparam name="T">The type of object contained in the list.</typeparam>
public class LoopList<T> : IList<T>
{
/// <summary>
/// The items in the LoopList.
/// </summary>
List<T> _items;
/// <summary>
/// Default LoopList constructor, takes an optional isReadOnly flag.
/// </summary>
/// <param name="isReadOnly">Whether this will be a read-only collection.</param>
public LoopList(bool isReadOnly = false)
{
_items = new List<T>();
IsReadOnly = isReadOnly;
}
/// <summary>
/// Constructor that takes an input collection to copy, also takes an optional isReadOnly flag.
/// </summary>
/// <param name="collection">The input collection to copy.</param>
/// <param name="isReadOnly">Whether this will be a read-only collection.</param>
public LoopList(IEnumerable<T> collection, bool isReadOnly = false)
{
_items = new List<T>(collection);
IsReadOnly = isReadOnly;
}
/// <summary>
/// Constructor that takes an initial capacity to allocate, also takes an optional isReadOnly flag.
/// </summary>
/// <param name="capacity">The initial capacity to allocate.</param>
/// <param name="isReadOnly">Whether this will be a read-only collection.</param>
public LoopList(int capacity, bool isReadOnly = false)
{
_items = new List<T>(capacity);
IsReadOnly = isReadOnly;
}
/// <summary>
/// Get the Index of an item in the LoopList.
/// </summary>
/// <param name="item">The item to find the index of.</param>
/// <<returns>The index of the specified item.</returns>
public int IndexOf(T item)
{
return _items.IndexOf(item);
}
/// <summary>
/// Insert an item at the specified index.
/// </summary>
/// <param name="index">The index at which to insert the item.</param>
/// <param name="item">The item to insert.</param>
public void Insert(int index, T item)
{
if (IsReadOnly)
{
throw new InvalidOperationException("Attempt to Insert item into ReadOnly LoopList");
}
_items.Insert(index, item);
}
/// <summary>
/// Remove the item at the specified index.
/// </summary>
/// <param name="index">The index of the item to remove.</param>
public void RemoveAt(int index)
{
if (IsReadOnly)
{
throw new InvalidOperationException("Attempt to Remove item from ReadOnly LoopList");
}
_items.RemoveAt(index);
}
/// <summary>
/// Get or set the item at a specified index.
/// </summary>
/// <param name="index">The index of the item to get or set.</param>
/// <returns>The item at the specified index.</returns>
public T this[int index]
{
get
{
return _items[index];
}
set
{
if (IsReadOnly)
{
throw new InvalidOperationException("Attempt to set item value in ReadOnly LoopList");
}
_items[index] = value;
}
}
/// <summary>
/// Add an item to the LoopList.
/// </summary>
/// <param name="item">The item to add.</param>
public void Add(T item)
{
if (IsReadOnly)
{
throw new InvalidOperationException("Attempt to Add item to ReadOnly LoopList");
}
_items.Add(item);
}
/// <summary>
/// Remove all items from the LoopList.
/// </summary>
public void Clear()
{
if (IsReadOnly)
{
throw new InvalidOperationException("Attempt to Clear ReadOnly LoopList");
}
_items.Clear();
}
/// <summary>
/// Check if the LoopList contains an item.
/// </summary>
/// <param name="item">The item to look for.</param>
/// <<returns>True if the LoopList contains the item, false otherwise.</returns>
public bool Contains(T item)
{
return _items.Contains(item);
}
/// <summary>
/// Copy the items in the LoopList to an array.
/// </summary>
/// <param name="array">The array to copy items into.</param>
/// <param name="arrayIndex">The index in the array to start copying items to.</param>
public void CopyTo(T[] array, int arrayIndex)
{
_items.CopyTo(array, arrayIndex);
}
/// <summary>
/// The number of items in the LoopList.
/// </summary>
public int Count
{
get { return _items.Count; }
}
/// <summary>
/// A flag indicating whether the LoopList is read-only.
/// </summary>
public bool IsReadOnly
{
get;
private set;
}
/// <summary>
/// Remove the specified item from the LoopList.
/// </summary>
/// <param name="item">The item to remove.</param>
/// <returns>True if the item was successfully removed, false otherwise.</returns>
public bool Remove(T item)
{
if (IsReadOnly)
{
throw new InvalidOperationException("Attempt to Remove item from ReadOnly LoopList");
}
return _items.Remove(item);
}
/// <summary>
/// Get an enumerator to loop over the items in the LoopList.
/// </summary>
/// <returns>An Enumerator over items of the type contained in the LoopList.</returns>
public IEnumerator<T> GetEnumerator()
{
List<T> tempItems = new List<T>(_items);
return tempItems.GetEnumerator();
}
/// <summary>
/// Get an enumerator to loop over the items in the LoopList.
/// </summary>
/// <returns>An Enumerator over items of the type contained in the LoopList.</returns>
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
And, voila! It works:
LoopList<int> list = new LoopList<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
List<int> toRemove = new List<int>() { 2, 4, 6, 8 };
foreach (int item in list)
{
if (toRemove.Contains(item))
{
list.Remove(item);
}
}