Came across an issue when writing some code to load my app's settings-- the following code produces an InvalidOperationException error:
For Each c As MapInfo In CurrentMaps
AddMap(c, False)
Next
The debugger highlights the Next part, and from the MSDN page on the error, it looks like this is because the collection (CurrentMaps) has somehow changed from when it last bumped the enumerator (to process the For Each).
Not sure why that would happen, though, as AddMap takes the MapInfo ByVal and doesn't touch the input (so the collection is not changed).
I changed it to this, which works perfectly fine:
For i As Integer = 0 To CurrentMaps.Count - 1
AddMap(CurrentMaps(i), False)
Next
Those should functionally work the same, right? Any idea why the For Each one doesn't work?
Edit:
Ah figured it out-- the problem is that I use CurrentMaps as a temporary variable (collection keeping track of current maps), and AddMap is usually used within the app to add a new map (which means both updating UI and adding it to CurrentMaps).
Here (when loading app), I'm just using AddMap to update the UI, but the sub call to add the item to CurrentMaps was still there, so it was indeed modifying CurrentMaps.
Thanks for the input, all!
No, they shouldn't work the same. You can't modify a collection while you're iterating over it, basically - and it looks like that's what's happening.
From the List<T>.GetEnumerator() docs:
An enumerator remains valid as long as the collection remains unchanged. If changes are made to the collection, such as adding, modifying, or deleting elements, the enumerator is irrecoverably invalidated and its behavior is undefined.
You haven't shown what your AddMap method does, but I suspect it adds a map to CurrentMaps, thus invalidating the iterator.
If you could give us more information about exactly what AddMap does, we can help you more. What does the False mean?
No these functions work very differently. The first version uses the .Net IEnumerable(Of T) + IEnumerator(Of T) pattern. It is not legal to mutate a collection while enumeratoring over it with this pattern. Many of the BCL types enforce this requirement.
The second simply uses a count + index pattern. Provided the bounds remain correct, it's legal to mutate the collection while enumerating with this pattern.
The problem here is almost certainly that AddMap is indirectly mutating the CurrentMaps collection and hence producing this error.
Ah figured it out-- the problem is that I use CurrentMaps as a temporary variable (collection keeping track of current maps), and AddMap is usually used within the app to add a new map (which means both updating UI and adding it to CurrentMaps).
Here (when loading app), I'm just using AddMap to update the UI, but the sub call to add the item to CurrentMaps was still there, so it was indeed modifying CurrentMaps.
Thanks for the input, all!
Private Maps as list(of MapInfo)
For each iMaps as MapInfo in CurrentMaps
Maps.add(iMaps)
Next
Related
Where in the VB6/VBA project references do Array(), LBound(), and UBound() come from..? When I'm typing in code, they don't appear in the Autocomplete list (ctrl+space), they don't get autocompleted, and they must be typed out completely before the text editor recognizes them. And only when a left-parenthesis is typed will ToolTipText pop up with the command syntax. Also, they do not appear anywhere in Object Explorer.
There's probably a basic concept in play here that I'm not aware of. And it makes me wonder, what other commands/statements/keywords are hidden in the same way..? Is there a list somewhere..? I googled for info but didn't find anything, probably because I don't know what I'm looking for and using the wrong search terms.
I ask these questions because I have the habit of prefixing many VB6 built-in functions like this: VBA.Left(), VBA.Len, VBA.Instr(), and so on. But I can't figure out what reference prefeix to use with Array(), LBound(), and UBound(), or perhaps they're so basic to VB6 that they don't have one.
I do this prefixing because years ago I was working on a large project, and there were functions I was trying to use with the same name in different reference libraries. I was a newbie and it took me a while to figure out, and it was causing tremendous problems since the functions were just NOT working the way I thought they were supposed to. It was then that I developed the prefixing habit after I figured it out. It's just easier that way, and always ensures the expected functions are being used.
The reason that they don't appear as IntelliSense options (and also why they don't appear in the Object Browser) is that they aren't declared in the VBE7.dll typelib for some reason that's beyond me. The Array function is implemented in the .dll as rtcArray. The utility of knowing that is dubious, in that its sole argument is a ParamArray, which means that if you called it directly from VBE7.dll you would need to create an array to have it feed you back the same array... This partially explains why it isn't on the typelib - a COM call would need to do the same thing, and the marshaling would basically be doing the same thing as what you'd expect the function to return.
LBound and UBound don't even appear as functions in the export table, so my guess is that they are handled more like "keywords" than first class functions internally. This makes some sense, in that it's fairly trivial to check the bounds of a SAFEARRAY if you have a pointer to the automation struct (you just index into the rgsabound array at the end of it and read the cElements and lLbound from it. Again a guess, but I'd assume that this allows for flexibility in letting LBound and UBound function with both fixed length and variable length arrays. In the fixed case, the array is basically managed as a block of memory with an indexer (more like a VT_CARRAY than a VT_SAFEARRAY). I'd imagine that handling this internally was easier or more convenient than providing first-class functions.
You won't find Debug in the Object Browser either, nor its methods Assert and Print.
You won't find Statements that are used like methods, like Open, Close, Get and Put, which is why you don't get any Intellisense when you use those statements, and the syntax must be memorized.
You will find Load and Unload as members of VBA.Global, but it's not clear what they belong to otherwise, and their arguments are late-bound Objects. The VBA documentation states that Load and Unload are Statements, even though the Object Browser shows them as Methods.
Keep in mind that you can move the order of references and it will make a difference. Try moving VBA to the top or near the top of your list of references. I believe that if something else also defines a BASIC keyword, it steals it, in a sense. I once had Right disappear and because I was not aware of the order of references, had to change all references of Right to VBA.Right. It's possibly the same with the ubound, lbound, or array.
VB in VS2008 under Windows 7 (64):
I need to change the value of a Property of a Component at some unpredictable time in DesignMode, and want the previously unknown new value to be embedded in the executable that results from VS compilation (as opposed to serializing it to some external file).
I have resorted to a text edit to swap the new value into the autogenerated Component initialization code in a prebuild event handler. This works fine, but it is a little hacky for my taste. Is there some way instead to force VS to refresh that text?
By luck, I found something that seems to work to force VS to autogenerate initialization code for the runtime instance of a Component, which is what I was after (I needed to have successful communication between designtime and runtime for Components -- easy for Controls, which use the latest designtime BackgroundImage bitmap at runtime (you need only to hide the Property value in the bitmap, which can be done entirely within the rules by using GetPixel and SetPixel). I considered various hacks, but I hit upon the following, which works and makes sense (though I might be completely FoS about the "why". If you know better, please educate me):
As I understand it, soon after a Component is dropped on a design surface in VS (and before it is rendered in the Component Tray), Visual Studio adds it to a collection of Components belonging to a Container. Adding it to the Container's collection is one step in a sequence of happenings that includes Visual Studio's autoregeneration of the Init procedure that will be used for the Component's root at runtime, and which includes values for the Public Properties of the Component. If you overload the Set Site procedure (the creation of ISite is an early step in that sequence) for your Component, and set a value for one of its Public Properties in the Overload, that value will show up in the autoregen text. This is almost what I wanted, except that it only worked when VS called Set Site, and I needed it to happen any time I chose.
Then I took a flyer, and in the UI that sets the Property value in question (at some unknowable time), I added code to remove the Component from the Container's collection and then re-add it, hoping that this might again set off a sequence of happenings that would lead to VS again autoregenerating the Init code, this time with the new value of the Property. It apparently did. Yay.
By deciding when to re-add a Component to the Container's Components collection, I am now able to force VS to write in the autogenerated Init text any value I assign to a Public Property of that Component, and hence embed the value in the executable when it is compiled.
This technique is vulnerable to changes in the (undocumented) way that Microsoft implements autogeneration, and so is arguably a hack. But even documented features are subject to change. Backward-compatibility is a nice idea, but sometimes it has to give way. And delivery is a requirement. It would be great to know that your code will still be good in any future version of VS, but that, sadly, can't happen, hack or no.
Of course, documented features are in general less subject to change than undocumented ones. But the logic of autogeneration after all the initial Property values are set is pretty compelling. That Microsoft uses the same sequence later on is not so inherently logical, but doing it a different way would cost Microsoft money for no apparent gain. And Microsoft and their ilk (are legally required to) make decisions based on the bottom line. So the status quo seems like a good bet.
I have a problem in understanding OOP...
This is it :
Sometime's you create an object with this syntax:
Object ObjectName = new Object();
But sometimes, we don't need to do that like in Android:
Textview TextviewName;
Or in J2ME:
form formName;
I already searched it and I got some information (but not sure) that this is because of static method... is it true? I think it has a relation with Polymorphism.. is it true?
Thanks all.
PS : Sory if I made some mistakes, English is not my native languange :D
Forget static methods - they're not relevant here. I'd advocate only looking at static methods / elements when you've truly grasped what objects are.
In Java, you can do this:
Object object;
Just as well as you can do this:
Object object = new Object();
In the first example, you're creating a reference but you're not populating that reference with anything, in the second example you're creating a reference and populating it with a new object, on which you can call methods, modify values and so on.
If you try and call methods on the first declaration you won't be able to - there's nothing there. Depending on the language and how you've declared it this might produce an error at runtime or a compile time error (Java does both depending on whether it's a field or a local variable.) The principle though is the same for all OO languages, you can't dereference (call methods, fields, etc.) on a reference that hasn't been populated, because in effect you're trying to call a method on something that isn't there.
you are mixing different languages and it's not the case of static methods nor polymorphism..
i suggest to read a good book of OOP beginning with the basis.. you can find "Thinking in c++" for free on the net..
Your Textview would not be initialized. Any try at using it would result in a NullReference error. In order for an object to actually be created, you have to use the new syntax or a function that returns a valid object.
However, this is a syntax-dependent issue, so first decide what language you want to study. If your Textview had been declared in C++, it would actually create an object, on the stack.
I have a label control on a form named lblTotal
During my last commit to the repository I was surprised to see this form's name on the modified list.
When I looked through the code I saw that all occurrances of this control's name were now lbltotal, (ie Small t).
The control's name itself was lblTotal in the properties window.
Further when I used ctrl-F with case matching to find lbltotal it would only find one as instantly all other occurrances would revert to lblTotal.
So it seems that at some moment after the IDE records some event in the code window it gets around to making the casing conform to the control's name.
What I don't understand is how this happened in the first place without a change to the control's name?
Can someone explain how?
PS After I wrote the above I saw this. Interestingly I also had a string variable that's name had its casing changed during the same commit and I assumed I must have changed it myself but now, having read the above I think it's because the same variable name was present in a different scope.
However there is no other occurrance of lbltotal anywhere else in the project.
PPS lblTotal is actually lblTotal(0) and many more copies are loaded during runtime, so a lot of the code references are lbltotal(x1).Caption etc. I don't know if this makes any difference.
Finally has anyone ever seen vb6 change the casing of string literals by itself? (Please God no!!)
Actually this last point is what causes me the most unease about this experience as in TOO many places in the codebase string literals are used in conditional clauses.
On top of the behaviour mentioned, VB6 will also re-case variable names that happen to occur in any of the COM interfaces your program references, which means that if you reference a new library, or change their order, you're in for even more fun.
At one time I even wrote a small program that "right-cased" VB6 source code from a dictionary of names ; the main drawback to that is you have to run it before each source commit.
But I think the answer from wqw here -> https://stackoverflow.com/a/1078018/108753 is probably the best solution to the problem I've seen, because he has quantified and exploited the behaviour of the IDE to solve the problem.
At least it's not VB3 - VB3 re-cases every variable in any files you have loaded into memory to what you last typed. Which makes you a lot more precise about typing case in variable names....
Hello i Have a programa that has starts in a main sub, it executes some code and after that code it opens a form using
System.Windows.Forms.Application.Run(General)
General is a form but also a class, so I wonder what it the pros or cons of using :
System.Windows.Forms.Application.Run(General)
vs
Dim gen as general
System.Windows.Forms.Application.Run(gen)
In the first I am opening a form using the name of the class, and I read that it is best to declare the instance as an object variable.
So far I used the first approach without any problem.
Thanks for your comments !
Yes, your first code snippet gives OOP purists the shivers. Application.Run() requires a form object, you are passing the form type. That this is possible is an anachronism carried over from VB6. It is admittedly very bad style, it prevents programmers from understanding the difference between a type and an object. Something that's crucial to understand to get ahead with object oriented programming. It is also quite lethal when you start using threads.
Your second snippet is closer to what's needed, except that it cannot work. You pass an uninitialized object to Run(). Fix:
Dim gen as General = New General()
System.Windows.Forms.Application.Run(gen)
Or taking advantage of VB.NET syntax:
Dim gen as New General
System.Windows.Forms.Application.Run(gen)
Or since you never actually need the object in the Main method:
System.Windows.Forms.Application.Run(New General())
In the last snippet, you're passing a null reference to the Run() method. Don't forget to create an instance of the form first.