Reputation: 15307
The following code:
var rnd = new Random();
Enumerable.Range(1,20).Select(x => rnd.Next(10)).ToDictionary(x => x, x => "");
will inevitably fail with the following exception:
System.ArgumentException: 'An item with the same key has already been added.'
Is there any way to use the Visual Studio IDE while debugging, to find out the attempted key? Is the key stored somewhere in the exception details -- some internal / private field perhaps; which can be read using the exception details window?
I can't change the source of ToDictionary
to include the key in the thrown exception.
Note: I am aware that I can rewrite this in a debuggable fashion, using a For Each
loop instead of LINQ. But I am asking about the general case -- when this exception is thrown, and the source code is not under my control, is there some way to access the key?
Using VS 2017 Community Edition.
Upvotes: 3
Views: 7082
Reputation: 15307
It is possible using the IDE, at least for a System.Collections.Generic.Dictionary<TKey,TValue>
. Presumably the attempted key could be found in a similar fashion for other dictionary types:
Enable .NET Framework Source debugging. The instructions appear to be still valid, even though they are for Visual Studio 2013. (NB. I had to clear the symbol cache.)
The exception (at least for this code) will be thrown at the following line:
throw new ArgumentException(Environment.GetResourceString(GetResourceName(resource)));
Backtracking one level up in the call stack to this line, I get to the following block of code:
for (int i = buckets[targetBucket]; i >= 0; i = entries[i].next) {
if (entries[i].hashCode == hashCode && comparer.Equals(entries[i].key, key)) {
if (add) {
ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_AddingDuplicate);
}
Most of the local variables are unavailable -- the Watch and Local windows give the following message:
Cannot obtain value of the local variable or argument because it is not available at this instruction pointer, possibly because it has been optimized away.
But i
is available, and so is entries
. The following expression in the Watch window returns the duplicate key:
entries[i].key
Upvotes: 5
Reputation: 1499890
I don't believe it's feasible to get that information from the exception with the current desktop .NET framework. (Although you can do it in the debugger, if you're willing to jump through some hoops.)
The good news is that in .NET Core, you do get to see the key, as you can see by following the source code:
Dictionary.Add
Dictionary.TryInsert
ThrowHelper.ThrowAddingDuplicateWithKeyArgumentException
SR.Argument_AddingDuplicateWithKey
So on .NET Core you end up with an exception like this:
System.ArgumentException: An item with the same key has already been added. Key: x
I don't believe you can programmatically determine the key at this point, but at least you can tell for debugging purposes, assuming the string representation of your key is sufficiently detailed.
I'd hope that at some point this might make it into the desktop version of .NET.
Upvotes: 7