This question already has answers here:
Why can I access an item in KeyCollection/ValueCollection by index even if it doesn't implement IList(Of Key)?
(2 answers)
Closed 6 years ago.
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.
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])
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.
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
Related
I have this dummy code in vb.net
Sub dummy()
Dim a = New List(Of XDocument)
Dim b = New List(Of Net.Mail.MailAddress)
b.AddRange(a)
End Sub
Obviously, this cant ever work. but the compiler ignores it.
How can I force VS to flag this at compile time?
Thanks
The compiler only allows that because you have Option Strict Off. You should basically ALWAYS have Option Strict On at the project level and only set it Off at the file level on the very rare occasions that you actually need to use late-binding. Even then, you should only set it Off in those specific files that actually require it and you should use partial classes to keep the code in those files to an absolute minimum.
Set Option Strict On in your project properties and the compiler will correctly flag that as bad code and there's every chance that you'll see other problem areas highlighted too. You should also set it On in the VS options, so that it will be On by default for all future projects.
Base Reference: Ten Code Conversions for VBA, Visual Basic .NET, and C#
Note: I have already created and imported a *.dll, this question is about aliases.
Let's say the programmatic name of a Test class is TestNameSpace.Test
[ProgId("TestNamespace.Test")]
public class Test ...
Now, say a C# solution has been sealed and compiled into a *.dll and I'm referencing it in a Excel's VBE. Note: at this point I cannot modify the programmatic name as if the *.dll wasn't written by me.
This is in VBA : Instead of declaring a variable like this:
Dim myTest As TestNameSpace.Test
Set myTest = new TestNameSpace.Test
I'd prefer to call it (still in VBE)
Dim myTest As Test
Set myText = new Test
In C# you would normally say
using newNameForTest = TestNamespace.Test;
newNameForTest myTest = new NewNameForTest;
Note: Assume there are no namespace conflicts in the VBA project
Question: is there an equivalent call in VBA to C# using or VB.NET imports aliases?
Interesting question (constantly using them but never thought about their exact meaning). The definition of the Imports statement (same for using) is pretty clear: its only function is shortening the references by removing the corresponding namespaces. Thus, the first question to ask is: has VBA such a thing (namespaces) at all? And the answer is no, as you can read from multiple sources; examples: Link 1 Link 2
In summary, after not having found a single reference to any VBA statement doing something similar to Imports/using and having confirmed that VBA does not consider the "structure" justifying their use (namespaces), I think that I am in a position to say: no, there is not such a thing in VBA.
Additionally you should bear in mind that it wouldn't have any real applicability. For example: when converting a VB.NET code where Imports might be used, like:
Imports Microsoft.Office.Interop.Word
...
Dim wdApp As Application
the code would be changed completely, such that the resulting string will not be so long:
Dim wdApp As Word.Application ' Prefacing the library's display name.
I think that this is a good graphical reason explaining why VBA does not need to have this kind of things: VB.NET accounts for a wide variety of realities which have to be properly classified (namespaces); VBA accounts for a much smaller number of situations and thus can afford to not perform a so systematic, long-named classification.
-------------------------- CLARIFICATION
Imports/using is a mere name shortening, that is, instead of writing whatever.whatever2.whatever3 every time you use an object of the given namespace in a Module/ Class, you add an Imports/using statement at the start which, basically, means: "for all the members of the namespace X, just forget about all the heading bla, bla".
I am not saying that you cannot emulate this kind of behaviour; just highlighting that having an in-built functionality to short names makes sense in VB.NET, where the names can become really long, but not so much in VBA.
The answer is no: there is a built-in VBE feature that recognizes the references added to a project and creates aliases at run-time(VBE's runtime) if there are no name collisions
In case of name conflicts in your registry all . dots will be replaces with _ underscores.
» ProgId's (Programmatic Identifiers)
In COM, it is only used in late-binding. It's how you make a call to create a new object
Dim myObj = CreateObject("TestNamespace.Test")
» EarlyBinding and LateBinding
In early binding you specify the type of object you are creating by using the new keyword. The name of you object should pop up with the VBA's intellisense. It has nothing to do with the ProgId. To retrieve the actual namespace used for your object type - open Object Explorer F2 and locate it there
This article explain where the names come from in Early Binding Section
use the same link for When to use late binding
for MSDN Programmatic Identifiers section please see this
I created an extension method in VB.NET in Visual Studio 2013 but it will not show up in Intellisense.
Imports System.Security.Claims
Imports System.Runtime.CompilerServices
Imports Connect.Common.Portable
Namespace Authorization
Public Module ClaimsPrincipalExtensions
<Extension()>
Public Function CurrentFirmNumber(ByVal principal As ClaimsPrincipal) As Integer
Dim c As Claim = principal.FindFirst(AuthorizationClaimTypes.LOGGED_IN_FIRM_NUMBER)
If (c IsNot Nothing) Then
Dim firmNumber As Integer = 0
If (Integer.TryParse(c.Value, firmNumber)) Then
Return firmNumber
End If
End If
Return 0
End Function
End Module
End Namespace
I have tried everything I could find on StackOverflow and other sites to solve this to no avail, including everything mentioned here.
In my calling code I include the right namespace, and the compiler and runtime are perfectly happy if I call this extension. That is, the code runs fine and correctly calls the extension method. It is simply intellisense that doesn't show it to me.
However, if I try to reference the method directly using the full namespace, as opposed to from the extended object, it shows up in intellisense.
Any ideas?
UPDATE:
So, it gets weirder. I had a colleague open up the same source and he gets intellisense.
As well, original extensions that used to work for me now have the same intellisense problem. However, if I start typing out the extension name intellisense does see it once it becomes a unique name.
For example, If I type "CurrentPrincipal.Current.C" Intellisense suggests "Claims", but not "CurrentFirmNumber", as that is part of the type. However, if I add a "u" it then suggests "CurrentFirmNumber", which is my extension.
According to the Microsoft documentation, to determine the number of characters in str, use the Len function. If used in a Windows Form, or any other class that has a Right property, you must fully qualify the function with "Microsoft.VisualBasic.Strings.Right".
If I set "Imports Microsoft.VisualBasic" at the top of the form I still have to use the fully qualified name in my code. Why does MS require this?
Because, without the fully qualified name, if there are two methods with the same name, the compiler cannot choose one over the other. So you should take care of the problem giving the correct hint
To ease your typing you could add at the top of your code file this version of the Imports statement
Imports VB6 = Microsoft.VisualBasic
and then you could type
Dim stringLen = VB6.Len(yourStringVariable)
This is the MSDN introduction to Namespaces in VB.NET, in particular, in the first lines of the article is explained your problem Avoiding Namespaces Collisions
NET Framework namespaces address a problem sometimes called namespace
pollution, in which the developer of a class library is hampered by
the use of similar names in another library. These conflicts with
existing components are sometimes called name collisions.
For example, if you create a new class named ListBox, you can use it
inside your project without qualification. However, if you want to use
the .NET Framework ListBox class in the same project, you must use a
fully qualified reference to make the reference unique. If the
reference is not unique, Visual Basic produces an error stating that
the name is ambiguous.
And by the way, start to use the equivalent framework methods for Right, Left, and Len.
They are still available only to help the porting of old VB6 application, (and sometime they work differently). In new applications I suggest to use
string.Substring(start, len)
string.Length
A winform, Form (derived from Control), have properties named Right and Left.
Public Class Form1
Inherits Form
Public Sub Test()
Dim location_left As Integer = Me.Left
Dim location_right As Integer = Me.Right
'Or simply:
location_left = Left '<- (Referring to Me.Left, not Microsoft.VisualBasic.Strings.Left)
location_right = Right '<- (Referring to Me.Right, not Microsoft.VisualBasic.Strings.Right)
End Sub
End Class
Therefore you'll need the use the full qualify name.
I just installed Visual Studio 2010 Service pack (proposed on Windows Update), and I can see a new feature on the "intellisense" that means when I write a Function or Sub in VB.NET it doesn't auto-complete parameters with ByRef or ByVal...
1) Is there anyway that I can configure this option back to how it was before?
2) If I don't specify ByX, which one is used by default? (it seems like it is always ByRef)
It seems that this post covers your question:
http://msmvps.com/blogs/carlosq/archive/2011/03/15/vs-2010-sp1-changing-quot-byval-quot-vb-net-code-editor-experience.aspx
So no, there is no way to get the old behaviour. From now on ByVal is the default (what it was before) and it won't get added automatically to the method parameters.
In my opinion this is a good decision since it's making VB.NET a bit more consistent with C# and avoids unnecessary "noises"(it's already verbose enough).
Old behaviour:
Private Sub test(ByVal test As String)
End Sub
New behaviour
Private Sub test(test As String)
End Sub
Tim covered what you asked directly, but something else to keep in mind is that any reference type variable, like a user defined class even if passed by value will allow you to make changes to that instances properties etc that stay. It won't however allow you to change the entire object. Which may be why it seemed to you to be defaulting to by reference
Public Sub (Something As WhateverClass)
Something = New WhateverClass 'will result in no changes when outside this method
Something.Property1 = "Test" 'will result in an updated property when outside this method
End Sub
From MSDN:
The value of a reference type is a pointer to the data elsewhere in memory.
This means that when you pass a reference type by value,
the procedure code has a pointer to the underlying element's data,
even though it cannot access the underlying element itself. For
example, if the element is an array variable, the procedure code does
not have access to the variable itself, but it can access the array
members.
Beware when transferring routines to VBA, where the default is ByRef (see, e.g., "The Default Method Of Passing Parameters" at the bottom of this page, by the great Chip Pearson).
That can be messy.