The first version of SafeList tried to address a common problem: inserting and removing elements into a List<T> while you loop over it. It had a lot of problems though and ended up being pretty much useless. Today’s article presents SafeList 2.0, a radically-improved version that really solves the problem so you can actually use it as a drop-in replacement for List<T>. Read on for the details, the source code, and even the unit tests that prove it handles all the nasty corner cases for you!

Let’s really briefly discuss the problem that SafeList fixes for you. Any time you add or remove elements to a List<T> while you’re looping over them, you have a problem. A foreach loop will throw an exception on the next iteration. You could use a for loop, but you still have to manually fix up your index (i) variable. You also sacrifice the cleaner (and sometimes faster) foreach syntax.

But if it were just these minor annoyances then SafeList wouldn’t be of much use. It’s the complex cases that really make it worthwhile. Consider this innocent-looking function:

void Iterate(Action<int> callback)
{
	for (var i = 0; i < list.Count; ++i)
	{
		callback(list[i]);
	}
}

How do you know that callback didn’t add or remove elements? How do you know that Iterate wasn’t called by another function that was also looping over the same list? If anyone else was messing with the list, it’s super hard to keep everybody’s index variables synchronized.

SafeList 1.0 handled these cases by providing a class with an Iterate function so it could keep track of loops over the list. It had functions like Add and Remove that queued up the added and removed elements until after all loops were done and then applied all the queued adds and then all the queued removes. This led to various problems including the list getting out of sync and added elements not being included in ongoing loops.

The second major problem was that SafeList didn’t implement any of the interfaces that List<T> does. This meant you couldn’t use it like an IEnumerable and foreach loop over it. You couldn’t add elements at construction time like this: new List<int>(){ 1, 2, 3 }. It also didn’t implement hardly any of the many useful methods found on List<T>.

All of these problems have been fixed for version 2.0.

The new-and-improved SafeList implements all of the interfaces that List<T> does: IList<T>, IList, ICollection<T>, IEnumerable<T>, IEnumerable, and ICollection. It also implements all of the extra functions that List<T> provides. So you get the full functionality and adaptability of List<T> in a safer package.

Internally, SafeList now uses a for loop and adjusts index variables as shown above. Because the methods of SafeList are the only way to add or remove elements to the list, it can adjust the index variables of all known loops. It’s still possible to write a broken for loop that indexes in to SafeList, but any foreach loop or loop using the ForEach or GetEnumerator methods is covered and doesn’t need to worry about changes to the list.

Also unlike the original SafeList, version 2.0 comes with NUnit-based unit tests. These can be easily run using Unity Test Tools or any other NUnit runner since SafeList does not depend on Unity.

As for usage, SafeList is meant to be a drop-in replacement for List<T>, so you can use it in exactly the same ways. Legally, usage should follow the MIT license. It should easily fit into almost any project.

Finally, I’ve uploaded both SafeList and its unit tests to a new GitHub project. Feel free to fork and send me pull requests with any enhancements you’d like to add, or simple leave a comment instead.

I hope you find SafeList useful in your projects. If you’ve got any questions about it or suggestions, please comment!