Careful What You Watch For
Can the simple act of creating a Watch change the behavior of your code while debugging? Why yes, yes it can.
I came across this answer on StackOverflow the other day and wanted to bring some attention to it (emphasis mine):
I had this problem manifest itself while debugging when I had a watch that attempted to return the "missing" key's item. Actually, further frustrated debugging had the same problem when I literally had a watch for the [scriptingdictonaryObject].exists() condtional); I suggest that the "missing" key is added because of the watch. When I removed the watch and instead created a temporary worksheet to copy the array to while running, the unwanted keys were no longer added.
What's going on here?
One of the "features" of a Dictionary object is that it will implicitly create new items without needing to call the .Add method explicitly. What's the difference between implicit and explicit?
Note: Early-bound usage of the Dictionary object requires a reference to the "Microsoft Scripting Runtime" (details here).
Dim MyDict As New Dictionary
'Explicit add
MyDict.Add "KeyA", "Item A"
'Implicit add
MyDict.Item("KeyB") = "Item B"
Debug.Print MyDict("KeyA"); vbNewLine; MyDict("KeyB")
Here's the relevant portion of the documentation regarding implicit key creation:
Remarks
If key is not found when changing an item, a new key is created with the specified newitem. If key is not found when attempting to return an existing item, a new key is created and its corresponding item is left empty.
Reproducing the problem
Let's reproduce the problem to see exactly where things go sideways.
Expected behavior
Create the following sample routine:
Sub WatchOut()
Dim MyDict As Dictionary
Set MyDict = New Dictionary
Debug.Print "KeyA exists? "; MyDict.Exists("KeyA")
End Sub
Run the above routine from the immediate window and it should return False:
WatchOut
KeyA exists? False
Add a watch
Now, let's add a watch of the "KeyA" item:
Let's try running the WatchOut routine again:
WatchOut
KeyA exists? False
So far, so good. Maybe this isn't a problem after all.
Step through the code
Let's add a Stop statement to force the code to break:
Sub WatchOut()
Dim MyDict As Dictionary
Set MyDict = New Dictionary
Stop
Debug.Print "KeyA exists? "; MyDict.Exists("KeyA")
End Sub
Now, let's try running the WatchOut routine:
WatchOut
KeyA exists? True
Aha! The combination of the watch and breaking into the debugger is enough to force the "bug" to appear. I put bug in scare quotes because it's actually expected behavior for the debugger. But it's almost certainly unexpected behavior for the developer.
(Note that there is nothing special about the Stop command that causes this behavior. You can remove the Stop line and set a breakpoint on the code and the same behavior will occur.)
You can see where this sort of thing could cause you to pull your hair out while debugging. Anytime the behavior of your program is different while running normally versus while debugging, you've got the makings of one aggravating debugging session.
Summary
Steps to reproduce the problem:
- Create a Watch for a specific Dictionary item
- Break into the debugger while executing the code
This will probably only ever help one or two developers. But it will potentially save those developers hours of frustration. And, if I'm being honest, I'm as likely as anyone to be one of those developers ;-).
Image by WikiImages from Pixabay