I'm not much of a Visual Basic person, but I am tasked with maintaining an old VB6 app. Whenever I check out a file, the editor will replace a bunch of the uppercase variable names with lowercase automatically. How can I make this stop!? I don't want to have to change them all back, and it's a pain to have these changes show up in SourceSafe "Differences" when I'm trying to locate the REAL differences.
It is changing it automatically in the definition, too:
Dim C as Control becomes Dim c as Control. Dim X& becomes Dim x&. But it doesn't do it all the time; for example, three lines down from Dim x&, there's a Dim Y&, uppercase, which it did not change. Why's it do this to me?
Since I always find this thread first looking for a solution to messed-up casing, here is one Simon D proposed in a related question:
If you just need to fix one variable's casing (e.g. you accidentally made a cOrrectCAse variable and now they are all over the place), you can correct this for good by adding
#If False Then
Dim CorrectCase
#End If
to the beginning of your module. If you save the project and remove the statement later, the casing remains correct.
Using Excel VBA I often accidentally change all Range.Row to Range.row by carelessly dimming a row variable inside some function - with the help of Simon D's solution I can fix this now.
Continuing from DJ's answer...
And it won't only change the case of variables in the same scope either.
It will change the case of all variables with the same name in your entire project. So even if they're declared in uppercase in one place, another module might have different variables using the same variable names in lowercase, causing all variables in your project to change to lowercase, depending on which of the declarations was loaded (?) or edited last.
So the reason your C and X variables are changing case, while the Y isn't, is probably because C and X are declared somewhere else in your project too, but in lowercase, while Y isn't.
There's another mention of it here, where they mostly seem concerned with such variable names conflicting when case is being used to differentiate local from global variables. They end up going for prefixes instead.
The only alternative I can think of is to use some other editor with VB6-highlighting capabilities to do your editing...
Enums are even worse. Getting the case wrong anywhere the enum is used changes the case of the definition.
To get past the painful file diff experience, set the VSS option in the diff dialog to do case-insensitive comparisons. That way you'll only see the "real" changes.
It must be defined/declared in lower-case. Locate the declaration and fix it there. The IDE will always change the case to match the declaration.
Close all the VB projects, open the form file with a text editor, change the case of all the names then re-open the Project with VB IDE.
Prevent VB6 auto correct For enum values
As my general rule I declare constants in UPPERCASE. Since enums are essentially constants I like to declare values for enums in UPPERCASE as well. I noticed the VB6 IDE also auto corrects these.
I found the IDE does not correct these values when using numbers and underscores '_' in the value names.
Example:
Public Enum myEnum
VALUE 'Will be corrected to: Value
VALUE1 'Will not be corrected
VALUE_ 'Will not be corrected
End Enum
I do not know if this works in general and if this extends to naming/auto correction of variable names.
I have had a similar enumeration problem where for no apparent reason UPPERCASE was changed to MixedCase.
Enum eRowDepths
BD = 1
CF = 1
Separator = 1
Header = 3
subTotal = 2
End Enum
When I changed to the following (tailing the last character of the non-conforming variables), I had no problem
Enum eRowDepths
BD = 1
CF = 1
SEPARATO = 1
HEADE = 3
SUBTOTA = 2
End Enum
It turns out that this is a case of the tail wagging the dog. I have the following code, not the most elegant I admit but working nonetheless (please excuse formatting issues):-
'insert 3 rows
iSubTotalPlaceHolder = i
rows(ActiveSheet.Range(rangeDMirrorSubTotals).Cells.Count + _
Header _
& ":" _
& ActiveSheet.Range(rangeDMirrorSubTotals).Cells.Count + _
Header + _
subTotal + _
Separator).Insert
So it seems that the compiler won't accept explicit UpperCase constants as part of this statement.
This was acceptable
Dim fred as Integer
fred = SEPARATO + HEADE + SUBTOTA
So my work-around is to use a variable instead of the constants as part of the complex insert statement if I want to stick to the rule of keeping enumerated constants uppercase.
I hope this is of use
DJ is spot on... VB always changes the case of variables to match the original declaration. It's a 'feature'.
Continuing from Mercator's excellent answer...
I'd recommend:
Check out all files (I assume you're using VSS for a VB6 app)
Do a rebuild of the entire project group
Recheck back into VSS
Now base you're the real differences rather than the 'auto' changes that VB6 has tried to apply.
Related
I usually see in almost all of VBA codes all variables are declared after e.g. Sub/Function name line
I know and I used variable declaration in the middle of some of my codes (Not inside a loop) and saw no problems.
I usually avoided that because I see most of VBA example codes have them declared right after the first line. I just want to know what are the risks from an expert/experienced VB programmer point of view.
There are no risks of declaring it in the middle.
The effect of declaring a variable in the middle is that it can only be used after that point and not before (which is scope).
The lifetime of the variable is different: the variable is created (allocated and initialized to its respective flavour of zero) when you enter the procedure, but you may not actually use it until you reach its scope (the point in the procedure where it's declared).
Declaring inside or outside a loop does not make a difference in VB6/A as they do not have block scope, unlike VB.NET.
So there is no performance difference between the two approaches (because all variables are created when you enter the procedure), but there is a difference in usage (you may not use a created variable before its declaration line). If you think that distinction is helpful in making sure you are not using a variable wrongly, declare your variables only where needed. Otherwise you are free to pick any of the two approaches, just apply it consistently (it's probably not a good idea to declare most of the variables in the beginning and then some in the middle).
Declare your variables, when you actually need them. When you have all declarations lumped at the top of the procedure, refactoring becomes much harder. And when you want to double check your declaration as you read your code (or, perhaps, someone else), searching it at the top may be again quite inconvenient, unless you procedure is short.
I would try to declare variables in a location that conveys useful information to the next programmer, over and above being functionally correct. This normally means: follow the scoping rules of the language.
By declaring all variables at the top you are making them available (in scope) for the entire procedure. That increases the work for a reader in the future, trying to understand how they will be used. Better to have them as local as possible.
I would not declare them in a loop since that actually would not have significance in VB6/VBA - but someone else might find confusing or misleading, or worst case it may cause subtle bugs.
Of course remember that this is not the only coding practice that we should be mindful of - if the procedure is so long that the location of the variable declarations is a big problem, that's a really good sign that the procedure should be broken up into smaller discrete logical blocks. The variable declarations would just be a symptom, not the main cause.
IMO there were many bad programming practices back in the 90s and earlier when VBA/VB6 were invented, but the industry has significantly learned & improved since then. So code from that era (or inspired by it) is often not a good example.
Declaring your variables up front, at the top of your sub/function makes it easy for others (and perhaps for you if you come by the code after, say a month) to read and understand what your code needs to calculate, and what placeholders/variables are required for the code to function.
You can of course declare variables anywhere (as long as you remember not to use a variable unless you have actually declared it first). That can work, and it has no effect whatsoever on the performance of your code (unless your logic includes an early Exit Sub or Exit Function. In this case, there will be a difference in performance depending on if your code does actually allocate memory for the variables or not).
It just isn't good practice to declare some variables at the top then do some work, then declare another set of variables mid-code. There are exceptions of course. When the variable you declared mid-code is for a temporary use, or something like that.
Sub CalculateAge()
Dim BirthYear As Integer
Dim CurrentYear As Integer
'Code to fetch current year
'Code to get BirthYear from user/or document
'Code to report result
End Sub
Compare that with the following:
Sub CalculateAge2()
Dim BirthYear As Integer
'Code to ask the user or fetch the birth year from the document
Dim CurrentYear As Integer
'Code to populate currentYear
'Code to do the calculation and report result
End Sub
In the first example, there is a clear separation from variables and logic. In the second, everything is mixed.
The first example is a lot easier to read and understand, especially if you use a good naming convention.
If you look at how classes are written or defined, you will see properties usually are first declared, then methods/logic below. This is the common practice used to write code.
PS: In other languages, you can declare and assign variables in the same line. in C# or VB.Net you could say something like:
int Age = CurrentYear - BirthYear; //C#
Dim Age As Integer = CurrentYear - BirthYear 'VB.Net
This is great if you use a lot of temporary variables, that you don't intend to declare ahead of time or maybe it would be more clear if declared mid-logic. But that's not possible in VBA. You need a separate line to declare a variable, and another to assign a value. You end up with a lot of Dim ___ As ___ statements. You might as well move the declaration part somewhere else to reduce distraction while reading the logic. Again, this works best if you use a good and consistent naming convention. If not, you end up in a worse situation like:
Dim w As Integer
Dim a As Integer
a = 42 'we don't know what this variable is for
'but we know its type from the previous line
Some_Lines_Of_code_And_Logic
' more code
' more code
w = 2 'we don't know what (w) is for, and we have to
'look up its declaration to get a hint
'which might be tedious
as far as I know, I have to rename variables used in my VBA code "manually" using the search/replace function in the VBA editor (at least if I don't use an add-on like V-Tools, Speed Ferret, Rubberduck, etc.). "Replace all", which would be quick, might give you unwanted results in some cases. So you have to go through each instance manually always reorienting yourself where the search function jumped to etc.
So my question is: is there another, less time consuming, way? Maybe without the search&replace? Or maybe there an easy to remember pattern how to name a variable (object, function, procedure, etc.) so you are sure that the "replace all" option won't give you any unwanted results?
Thanks and Cheers!
First of all, make sure you use Option Explicit. When you do that, you can be slightly less careful while renaming - compiling your code will make sure that you didn't miss any variable names.
Second - use a good text editor to do your replace and create good regexes to identify your names. Expressions that make sure that you don't have the name as a part of another name.
Update after comment
Here's a little snippet to help you with export (and/or code analysis directly in VBE):
Sub AllCode()
Dim Component As Object
For Each Component In Application.VBE.ActiveVBProject.VBComponents
With Component.CodeModule
For i = 1 To .CountOfDeclarationLines
Debug.Print .Lines(i, 1)
Next i
For i = .CountOfDeclarationLines + 1 To .CountOfLines
Debug.Print .Lines(i, 1)
Next i
End With
Next
End Sub
Ok so I was in college and I was talking to my teacher and he said my code isn't good practice. I'm a bit confused as to why so here's the situation. We basically created a for loop however he declared his for loop counter outside of the loop because it's considered good practice (to him) even though we never used the variable later on in the code so to me it looks like a waste of memory. We did more to the code then just use a message box but the idea was to get each character from a string and do something with it. He also used the Mid() function to retrieve the character in the string while I called the variable with the index. Here's an example of how he would write his code:
Dim i As Integer = 0
Dim justastring As String = "test"
For i = 1 To justastring.Length Then
MsgBox( Mid( justastring, i, 1 ) )
End For
And here's an example of how I would write my code:
Dim justastring As String = "test"
For i = 0 To justastring.Length - 1 Then
MsgBox( justastring(i) )
End For
Would anyone be able to provide the advantages and disadvantages of each method and why and whether or not I should continue how I am?
Another approach would be, to just use a For Each on the string.
Like this no index variable is needed.
Dim justastring As String = "test"
For Each c As Char In justastring
MsgBox(c)
Next
I would suggest doing it your way, because you could have variables hanging around consuming(albeit a small amount) of memory, but more importantly, It is better practice to define objects with as little scope as possible. In your teacher's code, the variable i is still accessible when the loop is finished. There are occasions when this is desirable, but normally, if you're only using a variable in a limited amount of code, then you should only declare it within the smallest block that it is needed.
As for your question about the Mid function, individual characters as you know can be access simply by treating the string as an array of characters. After some basic benchmarking, using the Mid function takes a lot longer to process than just accessing the character by the index value. In relatively simple bits of code, this doesn't make much difference, but if you're doing it millions of times in a loop, it makes a huge difference.
There are other factors to consider. Such as code readability and modification of the code, but there are plenty of websites dealing with that sort of thing.
Finally I would suggest changing some compiler options in your visual studio
Option Strict to On
Option Infer to Off
Option Explicit to On
It means writing more code, but the code is safer and you'll make less mistakes. Have a look here for an explanation
In your code, it would mean that you have to write
Dim justastring As String = "test"
For i As Integer = 0 To justastring.Length - 1 Then
MsgBox( justastring(i) )
End For
This way, you know that i is definitely an integer. Consider the following ..
Dim i
Have you any idea what type it is? Me neither.
The compiler doesn't know what so it defines it as an object type which could hold anything. a string, an integer, a list..
Consider this code.
Dim i
Dim x
x = "ab"
For i = x To endcount - 1
t = Mid(s, 999)
Next
The compiler will compile it, but when it is executed you'll get an SystemArgumenException. In this case it's easy to see what is wrong, but often it isn't. And numbers in strings can be a whole new can of worms.
Hope this helps.
I have a macro which reads and writes data from two sheets in the same workbook.
Is it possible to clean up and simplify the code/statements to improve readability and aid in debugging efforts?
The statements have become so long they are confusing to read even when using the space-underscore method to use more than a single line.
Example of a statement which has become unwieldy:
Range("mx_plan").Cells(WorksheetFunction.Match(sortedAircraft.Item(i).tailNumber, Range("aircraft")), WorksheetFunction.Match(currentWeekId, Range("week_id")) + weekly_hours_col_offset) = (acft_hoursDNE / acft_weeksRemaining)
I've intentionally tried to avoid making explicit references to individual cells or ranges.
Your statement is 225 characters!
Debugging it will be impossible, because it's one instruction doing way too many things, and you can only place a breakpoint on a line of code... so you can't break and inspect any of the intermediary values you're using.
Break it down:
tailNumber = sortedAircraft.Item(i).tailNumber
aircraft = someSheet.Range("aircraft").Value
planRow = WorksheetFunction.Match(tailNumber, aircraft)
weekId = someSheet.Range("week_id").Value
planColumn = WorksheetFunction.Match(currentWeekId, weekId)
Set target = someSheet.Range("mx_plan").Cells(planRow, planColumn + weekly_hours_col_offset)
target.Value = acft_hoursDNE / acft_weeksRemaining
Remember to declare (Dim) all variables you're using (use Option Explicit to make sure the code won't compile if you make a typo with a variable name), use meaningful names for all identifiers (names that tell the reader what they're for - use comments when the why isn't obvious from the code alone).
By breaking it down into multiple smaller steps, you're not only making it easier to read/maintain, you're also making it easier to debug, because a runtime error will be raised in a specific instruction on a specific line, and you'll be able to more easily pinpoint the faulty inputs.
Use With ... End With statements to localize any Range.Parent property.
Declare and Set a variable to the Excel Application object that can be used as a replacement for the WorksheetFunction object. This should make repeated calls to worksheet functions more readable.
Bring everything to the right of the equals sign down to the next line by supplying a _ (e.g. chr(95)). This acts like a concatenation character and allows single code lines to be spread over two or more lines. I've also use it to line up the two MATCH functions which return row and column to the Range.Cells property.
Dim app As Application
Set app = Application
With Worksheets("Sheet1").Range("mx_plan")
.Cells(app.Match(sortedAircraft.Item(i).tailNumber, Range("aircraft"), 0), _
app.Match(currentWeekId, Range("week_id"), 0) + weekly_hours_col_offset) = _
(acft_hoursDNE / acft_weeksRemaining)
End With
Set app = Nothing
That looks significantly more readable to my eye. Your use of named ranges may also be improved but it is hard to make suggestions without knowing the parent worksheets that each belongs to.
Note: I added a , 0 to each of the MATCH functions to force an exact match on unsorted data. I do not know if this was your intention but without them the data in the aircraft and week_id named ranges must be sorted (see MATCH function).
Is it in VB.NET possible to use variables without the need of use DIM?
now I have to use the variables like this:
dim a = 100
dim b = 50
dim c = a + b
I want to be able to use vars in this way:
a=100
b=50
c=a+b 'c contains 150
I think in VB6 and older VB this was possible, but I am not sure.
As far as what #Konrad said, he is correct. The answer, buried in all his caveat emptors, is the answer of "yes", you can absolutely do this in VB.NET by declaring Option Explicit Off. That said, when you do a=1, the variable a is NOT an Integer - it is an Object type. So, you can't then do c = a + b without compiler errors. You'll need to also declare Option Strict Off. And at that point, you throw away all the benefits of a compiler. Don't do it.
As an alternative, with Option Infer On, Dim behaves the same as C#'s var keyword and gives you a lot of advantages if you're trying to save on typing.
You have a fundamental misunderstanding of how VB is supposed to work. The Dim statements are there to help you. Your wish to elide them is misplaced.
The compiler enforces variable declaration so that it can warn you when you have accidentally misspelt a variable name, thus inadvertently creating a new variable, and is required to enforce type safety. Without variable declaration, VB code becomes an unreadable, unmaintainable mess.
Incidentally, the same was true in VB6, and you should have used Option Explicit in VB6 to make the compiler force you to use them properly. This option still exists in VB.NET but switching it off has no advantage, and a whole lot of disadvantages so don’t do it – instead, learn to appreciate explicit variable declarations, and all the help that the compiler is giving you through them.