6

I have a VB.NET project where I am able to iterate through the keys and values collections of a dictionary object using an index:


MyDictionary.Keys(idx)
MyDictionary.Values(idx)

When this code is taken from the test project and placed into the real project I get the following error:

'System.Collections.Generic.Dictionary(Of Double, String).KeyCollection' cannot be indexed because it has no default property.

and

'System.Collections.Generic.Dictionary(Of Double, String).ValueCollection' cannot be indexed because it has no default property.

This is using VB.NET and VS 2008. I don't know what the difference would be from one project to the next that would cause this error. The test is a console application and the program is a winforms app.

What conditions would cause the default property of these collections to change?

Edit - Thank you for all of the answers that tell me how to loop through a dictionary. Those, answers, however, do not answer my question of why I can use an index in one project and not the other. Should I not be able to copy and paste the code from one .net project to another and have it work the same? And, no, option strict, is not the cause of the problem.

Edit - Attempt to reproduce what I'm seeing:

  • Create a new VB.NET Console Application using VS 2008
  • Copy and paste the following code into the module:

Imports System.Collections
Imports System.Collections.Generic

Module Module1

    Public dtf As Dictionary(Of Double, String)

    Public Sub BuildDictionary()

        dtf = New Dictionary(Of Double, String)

        dtf.Add(1.0, "1")
        dtf.Add(0.0, "0")

    End Sub

    Public Sub Search()
        For idx As Integer = 0 To dtf.Keys.Count - 1
            If dtf.Keys(idx) = 0 Then
                Exit Sub
            End If
        Next
    End Sub

    Sub Main()

    End Sub

End Module

In the line in sub search that says "dtf.Keys(idx) = 0" place your cursor after the right parenthesis and backspace you should get a tooltip that says, "<Extension> ElementAtOrDefault(index as Integer) as Double - index: the zero based element of the index to retrieve.

I am not getting that in my other project. Even though it seem I have the same references and settings.

user79755
  • 2,623
  • 5
  • 30
  • 36
  • according to the error, you are using a double as key? Is that intentional? – Mitch Wheat Apr 25 '09 at 03:09
  • @neodymium: Thanks for posting the details. I'll look into it. You should also give some detail about the "real" project. – John Saunders Apr 25 '09 at 15:08
  • @neodymium: now that you know what the problem was, I suggest you edit the subject line to reflect what was really going on. The problem actually had nothing to do with default properties. Chances are that someone else with the same problem will not find this question based on your current subject line. – John Saunders Apr 25 '09 at 15:44
  • @John Saunders: The original errors listed above do in fact have to do with default properties. If those are the errors you receive then that is probably what you would search for. If you feel there is a better title, then feel free to change it yourself. – user79755 Apr 25 '09 at 15:56
  • My problem was similar - a console app and a test project that couldn't match it's walking of the XElement tree. System.Xml.Linq was referenced in both, but when I added System.Linq to the test project it started working the same as the console app - even though the console app did NOT reference that library! WTF?! – Falkayn Jan 04 '11 at 20:48

3 Answers3

4

KeyCollection does not implement indexers like that, you must enumerate through the MyDictionary.Keys.

c#

foreach(double key in MyDictionary.Keys)
 Console.Write( MyDictionary[ key ] )

vb

For Each key As Double in MyDictionary.Keys
   Console.Write( MyDictionary( key )
Next key

Looping with a for(;i++;) wouldn't be the correct way of going through your hashtable (dictionary) since it is not an array it really has no concept of an array index (array[index])

Chad Grant
  • 44,326
  • 9
  • 65
  • 80
3

I bet your real project had OPTION STRICT ON, as all projects should, and that your test project had it OFF. That's why you didn't get a compiler error in your test project.

EDIT: the poster says he has OPTION STRICT ON for both projects. That makes this more interesting.

I still think the most likely reason for this difference is that in one case, the compiler compiled the code and saw the error; but in the other case, the compiler didn't comile the code. Is this the same version of Visual Studio on the same machine at the same time? Same .NET Framework version in both cases?

Are these both the same type of project, for instance, are they both console applications? I ask because ASP.NET Web Site "projects" usually don't attempt to compile code until the code is called. If your test project were such a "project", and if you didn't actualy test the code (that is, if you didn't actually step into this code and see it work), then you might have assumed that the fact you could press F5 meant that all the code was compiled, when it wasn't.

My next thoughts would be to see if MyDictionary was really of the same type in both cases.

Beyond that, if you really need to know why this happened, I'd make a copy of the "real" project, and start changing it to be more and more like the test project. This would probably be a matter of mass deletions at first. I'd keep changing it either until the problem was found, or until the two were identical.

EDIT 2: The default console project imports the System.Linq namespace (see the "References" tab in project properties). This import brings the ElementAtOrDefault extension method into scope. This extension method extends IEnumerable(Of T); in your case IEnumerable(Of Double), which is what the Keys property implements.

What surprises me about this is that VB.NET is automatically applying this extension method. In C#, the method would need to be explicitly named.

If you remove the Import of System.Linq, you'll find that your test application gets the same error as the production application.

John Saunders
  • 160,644
  • 26
  • 247
  • 397
  • I have option strict on in both projects. – user79755 Apr 25 '09 at 14:30
  • 1
    Yes, I was missing the System.Linq import. Thank you. – user79755 Apr 25 '09 at 15:27
  • You should move your `Edit2` to the top. It's the answer. Would be interesting to know where this strange and terrible behaviour is documented. ++ – Tim Schmelter Apr 29 '16 at 09:00
  • **Edit** VB.NET language specification 11.21.3: Default Query Indexer. Every queryable collection type whose element type is T and does not already have a default property is considered to have a default property of the following general form: Public ReadOnly Default Property Item(index As Integer) As T Get Return Me.ElementAtOrDefault(index) End Get End Property – Tim Schmelter Apr 29 '16 at 09:09
2

The Keys and Values property of Dictionary(Of TKey,TValue) do not have an indexer property. They are implementations of ICollection vs. IList and hence don't support accesses by Index. If you want to iterate through a Dictionary, the best way is a For Each loop.

For Each pair in MyDictionary
  Dim key = pair.Key
  Dim value = pair.Value
Next

EDIT

Have you checked to make sure that System.Core is referenced in both projects and that you have a project level imports for System.Linq? That's the only thing I can think of that would produce a difference in ElementAtOrDefault which is a method inside of system.Core.

I'm still a bit baffled why that method would be bound to for a simple indexer. Going to look into that

JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454