Stop wasting time digging into complex collections in the VS Watch Window!

I’ve always hated comparing two collection during debugging…

Working with Visual Studio’s Watch window is relatively easy when you’re working with small, flat collections – but understanding and finding data in complex collections with complex objects and/or thousands of items is nearly impossible!

You end up staring at something like this:

bigcomplexcollection

And then you find yourself spending your time expanding a million nodes and copying bits of information onto a piece of paper, just so that you can compare different bits of data and find out what state your code has changed. It’s a nightmare!

How did we get to this point?

A while ago, my team had a bug in our application:
After a collection of items were marked for removal, some items from that collection were left in the application’s memory – inside the application’s HashSet.
The hard part was figuring what happened to those items!

Thus, my journey to figure out the cause of the issue started!
Keep in mind that I can’t share actual code, instead I’ll share similar, mock code I’ve created to explain the issue we’ve faced and how we’ve managed to solve it.


The data being debugged

Each item has the following properties: two doubles, an enum and another class.
The collection item and the inner class are overriding the equals method and providing a comparison based on property values.

classdiagram

To illustrate the problem we had, I’ve wrote a small console application.
The main method looks like this:

SimpleItemsProvider simpleItemsProvider = new SimpleItemsProvider();
CopiedItemsProvider copiedItemsProvider = new CopiedItemsProvider();

List<CollectionItem> items = ItemsCreator.CreateItems(9481);
simpleItemsProvider.SetItems(items);
copiedItemsProvider.SetItems(items);

List<CollectionItem> simpleItems = simpleItemsProvider.GetItems();
List<CollectionItem> copiedItems = copiedItemsProvider.GetItems();

HashSet<CollectionItem> simpleItemsHash = new HashSet<CollectionItem>(simpleItems);
copiedItems.ForEach(copiedItem => simpleItemsHash.Remove(copiedItem));

Console.WriteLine($"Number of items left: {simpleItemsHash.Count}.");

In short, the above code do the following:

  1. Creates to different item providers.
  2. Gets items from a service. (ItemsCreator in our case)
  3. Sets the items to both providers.
  4. Gets the items from those providers.
  5. Convert one of the collections to a HashSet.
  6. Tries to remove all the items in the HashSet by using the other list of items.
  7. Prints the number of items left in the HashSet.

If we run this program, we can see right away the problem.
Since those items are the same, we expect the print text to be: “Number of items left: 0.”

However…
badresult


So, how should I tackle this?

First of all, we need to understand that those collection, although they should be, are not the same object.

Let’s start by checking the items I can’t remove in the simpleItemsHash HashSet.
To make my life easier, I’ll use OzCode’s reveal feature, which enables me to preview specific properties in objects, without digging for the data of each item.
Checking those 10 items turned out to be not as helpful as I wished, since there is nothing special about them.

These values are completely random, and all the values seemed plausible.

baditemswatch

What now? Let’s check what items are different between the two collection.
But how should I do it?
Sure, I can write a LINQ expression (Or god forbid a foreach loop which initializes a list),
which will check for each item in the copiedItems if it’s contained in the simpleItemsHash,

  1. Create a list out of it,
  2. Run this code,
  3. Check the differences.

Good thing OzCode has a simple compare feature, that shows the differences between the two collections:

comparecopytosimple-start comparecopytosimple-compare

Great! I see a pattern!

It’s clear here, that items from the copiedItems list with the EnumValue property set to “None” are different.
Looks like the CopiedItemsProvider is making trouble for us.
And indeed, checking the SetItems method in CopiedItemsProvider reveals the problem:

public void SetItems(List<CollectionItem> items)
{
    _copiedItems = items.Select(CreateItemCopy).ToList();
}

private CollectionItem CreateItemCopy(CollectionItem currentItem, int index)
{
    MyEnum copiedEnumValue = _randomIndexes.Contains(index) ? MyEnum.None : currentItem.EnumValue;

    return new CollectionItem(currentItem.Key, currentItem.X, currentItem.Y, copiedEnumValue);
}

Seems like someone decided to change some of the enum values while copying them in the CopiedItemsProvider.
Indeed, taking care of this line, fixes the problem.

private CollectionItem CreateItemCopy(CollectionItem currentItem, int index)
{
    return new CollectionItem(currentItem.Key, currentItem.X, currentItem.Y, currentItem.EnumValue);
}

Voila!

goodresult

I was pleasantly surprised by how easy it was fixing the collections comparison bug.
From my previous experiences, I knew it could take me quite a while to handle such things,
mostly because of the massive clicking around and code writing it took to accomplish the same exact thing I’ve just did in under 10 minutes.

 

Happy coding!

Alex Pshul

Software consultant and OzCode evangelist, with great passion to technology. For most parts of my career I was focusing on Microsoft technologies, Although Web development and DB could not escape from me either.