Odd runtime IllegalCastException when iterating over Dictionary

Consider the following code:

dim d as dictionary

d=new Dictionary

d.Value("theKey")="theValue"

for each p as pair in d
  break
next

This code will compile. However, when run, it throws an IllegalCastException on the Next statement, because Dictionaries don’t iterate Pair objects, they contain DictionaryEntry objects.

However, this code will fail compilation:

dim numbers() as integer

for each b as boolean in numbers
  break
next

Type mismatch on the For…Each line.

What’s the difference here? Seems like the Dictionary code should throw the type mismatch error during compilation. And if you change it to this, it does throw the correct error:

dim d as dictionary

d=new Dictionary

d.Value("theKey")="theValue"

for each i as integer in d
  break
next

Is it perhaps becuase, internally, a DictionaryEntry is a subclass of Pair…?

So there is an internal DictionaryIterator class, which returns value as a variant, so you won’t get compile error for whatever you write there.
The thing you get is a DictionaryEntry.

And DictionaryEntry is not a pair, but its own class.

2 Likes

DictionaryEntry inherits from Pair, so the compiler allows the assignment.
At runtime, it tries to cast each DictionaryEntry to a Pair, and this is where it fails, because For Each in Xojo requires an exact match or a valid cast.

To iterate a Dictionary safely, always use:

for each entry as DictionaryEntry in d
  dim key as variant = entry.Key
  dim value as variant = entry.Value
next

Or, if you want the result as a Pair, cast manually inside the loop (with error handling if needed).


Here’s a short utility function in Xojo that takes a Dictionary and returns an array of Pair objects — one for each key/value entry:

Function DictionaryToPairs(d As Dictionary) As Pair()
  Dim result() As Pair
  
  For Each entry As DictionaryEntry In d
    result.Add(entry.Key : entry.Value) ' Create Pair using colon syntax
  Next
  
  Return result
End Function

Usage Example:

Dim d As New Dictionary
d.Value("Name") = "Sascha"
d.Value("Role") = "Developer"

Dim pairs() As Pair = DictionaryToPairs(d)

For Each p As Pair In pairs
  System.DebugLog(p.Left.StringValue + " = " + p.Right.StringValue)
Next
1 Like

I don’t think this is quite the case, because the compiler won’t let you use an Integer variable as the iterator - it only lets you use Pair or DictionaryEntry. That’s why I suspect an inheritance relationship between those two classes.

Actually, it was a simple mistake on my part that got me here; I forgot that Dictionaries don’t iterate using Pairs.