views:

835

answers:

3

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.

+3  A: 

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
Your vb.net sample was broken. I fixed it.
Joel Coehoorn
@Joel, thanks ... I haven't done any VB in years :)
Chad Grant
This does not answer my question of why I can use an index in one project and not in another.
I wouldn't know, I would never want to use a hashtable by key index since that is not predictable. Index is irrelevant with hashtables and not even sure why MS would have implemented that in LINQ. You're going to be much, much safer/bug free using the Key instead of the index.
Chad Grant
and I see no reason at all to even use LINQ in this code
Chad Grant
+2  A: 

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
This does not answer my question of why I can use an index in one project and not in another.
Yes, I was missing the System.Linq import. Thank you.
A: 

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
I have option strict on in both projects.
Yes, I was missing the System.Linq import. Thank you.