Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 2 years ago.
Improve this question
I read the With-Statement documentation from Microsoft and still don't understand what the purpose of the With statement is...
Xpost from Reddit, thought this should really be somewhere on Stack Overflow and couldn't find any questions of this nature.
Introduction to With
Imagine the following code (Example A):
ThisWorkbook.Sheets(1).Range("A1").Font.Color = rgb(255,0,0)
ThisWorkbook.Sheets(1).Range("A1").Font.Bold = true
ThisWorkbook.Sheets(1).Range("A1").Font.Italic = true
ThisWorkbook.Sheets(1).Range("A1").Font.Size = 16
If we count the .s, in this example there are 13. This means that objects are accessed via a property accessor, 13 times in our code.
What happens when we use a With statement instead? (Example B):
With ThisWorkbook.Sheets(1).Range("A1").Font
.Color = rgb(255,0,0)
.Bold = true
.Italic = true
.Size = 16
End With
If we count the .s in the new code, we see property accessors are only called 7 times! So we just halfed the amount of work that VBA had to do by using a With statement!
This is the primary benefit of using the With statement.
How it works internally
Internally VBA is doing something like this (Example C):
Dim temp1 as Object
set temp1 = ThisWorkbook.Sheets(1).Range("A1").Font
temp1.Color = rgb(255,0,0)
temp1.Bold = true
temp1.Italic = true
temp1.Size = 16
set temp1 = nothing 'More specifically temp1 now calls IUnknown::release()
So you can actually imitate the behaviour of With with this code, but With uses "hidden variables" which don't pollute your local variable scope, so this may be preferred.
Performance Caveats
In regards to performance, the With statement can be used in such a way that it has a performance detriment instead of a performance benefit.
1. A is a local variable
Dim A as SomeObject
set A = new SomeObject
With A
.B = 1
.C = 2
.D = 3
End With
In this case A is a local variable. If we run through our conversion:
Dim A as SomeObject
set A = new SomeObject
Dim temp1 as SomeObject 'Unnecessary call
set temp1 = A 'Unnecessary call
temp1.B = 1
temp1.C = 2
temp1.D = 3
set temp1 = nothing
We see there is potentially some performance detriment in our code, because temp1 needs to be defined and set. The performance decrease will be relatively negligible compared to the property accessors though, so it's unlikely to be noticable. Note that the performance decrease is largely negligible because setting an object doesn't transfer the whole object, but transfer a pointer to the object, which is extremely performant.
PS: This performance degradation is just hypothetical at the moment, I'll confirm whether this is true or whether the performance is optimised away by the compiler.
2. A is a MEMBER variable
If we have a class with the following code:
Public A as object
Sub Test
With A
.B = 1
.C = 2
.D = 3
End With
End Sub
In this case A is a member/property variable. So actually some information is hidden. Let's correct that:
Public A as object
Sub Test
With Me.A
.B = 1
.C = 2
.D = 3
End With
End Sub
Ah, now we can see that by using With we are essentially saving 3 member accessor calls:
Dim Temp1 as object
set Temp1 = Me.A
Temp1.B = 1
Temp1.C = 2
Temp1.D = 3
set temp1 = nothing
vs
Me.A.B = 1
Me.A.C = 2
Me.A.D = 3
Ultimately what I'm saying is VBA may be using hidden variables that you can't see and thus what's really going on under the hood is very context specific. What you are doing may warrant the usage of With in some cases and others not. If in doubt, use With because the performance benefits of getting it wrong far outweigh the performance detriments otherwise.
Note: There are a few small benefits to the With statement regarding speed of writing code and representation of hierarchy, however these are mainly my personal opinion and do not belong here.
P-Code and Performance Measurements
In case interested, here are some dumps of the PCode for a set of examples with and without the With Statement
Of course the P-Code is only half the story as different P-Code operations will each have different speeds. Here's a comparison.
The first 3 columns display a test with the 3 cases described at the beginning of this post. The rest of the tests are relatively random but here's what I learnt:
Local Variables in a with statement appear to be much faster than accessing Member variables.
Using with has performance penalties for small number of calls.
As expected With A.O.O performs signficantly better than A.O.O.Prop=...
With actually has noticable speed difference. Therefore if you can try to keep your With statements outside of any loops you have!
Note: all tests ran on Mac version of VBA in Excel 2011
Note: All tests run over 10^8 iterations - this means these performance differences, although there, are tiny!!
Related
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).
Can I use Win API methods such as LVM_INSERTITEM and LVM_SETITEM for adding items in ListView control?
I want to load listview data fast, I think objListView.ListItems.add is slow for larger amount of rows to be added into the listview.
Currently I use following code to Add ListItems:
Dim tmpLI as ListItem
Set tmpLI = ListView1.ListItems.Add text:="Item" & iCounter
tmpLI.SubItems(1) = objRs("StudentCode")
tmpLI.SubItems(2) = objRs("StudentName")
tmpLI.SubItems(3) = objRs("MotherName")
tmpLI.SubItems(4) = objRs("FatherName")
tmpLI.SubItems(5) = objRs("PhoneNo")
etc.
tmpLI.SubItems(15) = objRs("Description")
[iCounter is a Loop Variable, objRs is ADODB Recordset]
You can do that, but it probably won't speed things up too much. You will probably find that use of a With block will give you a better result, though. Like this:
With ListView1.ListItems
.Add text:="Item" & iCounter
.SubItems(1) = objRs("StudentCode")
.SubItems(2) = objRs("StudentName")
.SubItems(3) = objRs("MotherName")
.SubItems(4) = objRs("FatherName")
.SubItems(5) = objRs("PhoneNo")
etc.
.SubItems(15) = objRs("Description")
End With
The reason that this is faster is that you only need to resolve the object reference once, at the top of the With block, instead of once for each line of code.
You might also get noticeable improvement with using objRs.Fields(0), objRs.Fields(1), etc. The runtime has to resolve the field name string to the offset of the Collection, which is an extra step. The compiler doesn't know about the database, so it can't do that at compile time. When you remove the extra step, you remove performance overhead. Whether it's enough to make a difference might be worth testing.
So there is a question on what DIM is, but I can't find why I want to use it.
As far as I can tell, I see no difference between these three sets of code:
'Example 1
myVal = 2
'Example 2
DIM myVal as Integer
myVal = 2
'Example 3
DIM myVal = 2
If I omit DIM the code still runs, and after 2 or 3 nested loops I see no difference in the output when they are omitted. Having come from Python, I like to keep my code clean*.
So why should I need to declare variables with DIM? Apart from stylistic concerns, is there a technical reason to use DIM?
* also I'm lazy and out of the habit of declaring variables.
Any variable used without declaration is of type Variant. While variants can be useful in some circumstances, they should be avoided when not required, because they:
Are slower
Use more memory
Are more error prone, either through miss spelling or through assigning a value of the wrong data type
Using Dim makes the intentions of your code explicit and prevents common mistakes like a typo actually declaring a new variable. If you use Option Explicit On with your code (which I thoroughly recommend) Dim becomes mandatory.
Here's an example of failing to use Dim causing a (potentially bad) problem:
myVar = 100
' later on...
myVal = 10 'accidentally declare new variable instead of assign to myVar
Debug.Print myVar 'prints 100 when you were expecting 10
Whereas this code will save you from that mistake:
Option Explicit
Dim myVar as Integer
myVar = 100
' later on...
myVal = 10 ' error: Option Explicit means you *must* use Dim
More about Dim and Option Explicit here: http://msdn.microsoft.com/en-us/library/y9341s4f.aspx
Moderators, I'm making an effort, assuming you'll treat me with due respect in thefuture.
All local variables are stored on the stack as with all languages (and most parameters to functions). When a sub exits the stack is returned to how it was before the sub executed. So all memory is freed. Strings and objects are stored elsewhere in a object manager or string manager and the stack contains a pointer but vb looks after freeing it. Seting a vbstring (a bstr) to zero length frees all but two bytes. That's why we try to avoid global variables.
In scripting type programs, typeless programming has many advantages. Programs are short and use few variables so memory and speed don't matter - it will be fast enough. As programs get more complex it does matter. VB was designed for typeless programming as well as typed programming. For most excel macros, typeless programming is fine and is more readable. Vbscript only supports typeless programming (and you can paste it into vba/vb6).
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 8 years ago.
Improve this question
code1
while
dim number as integer = 0
'some code here
end while
code2
dim number as integer
while
number = 0
'some code here
end while
What is the difference in code 1 and code 2 in terms of speed?
What else is their difference?
What is the best practice to use? thank you
In code 1, your variable's scope is constrained to the while-block.
In code 2, your variable's scope goes beyond the while-block. For example if you define your while block within a function, the scope of the variable is the whole function.
You can note the difference if you have the same variable within multiple blocks:
while
dim number as integer = 0
x = number // x is 0
number = 1
end while
while
dim number as integer = 0
x = number // x is 0
end while
This code is fine, where as the following
dim number as integer = 0
while
x = number // x is 0
number = 1
end while
while
x = number // x is 1
end while
Both ways are fine. Speaking of performance - do not care, if you need to improve the performance of your code you will most likely need to touch a different place. Things like this are therefore often called "micro-optimizations".
Speaking of practice, it is usually best to define the variable as close to its use as possible. So if you only need the variable (and its state) within your while-loop, define it there. If you need to read the value after the while-loop, define it outside. If you use a tool like ReSharper it will even suggest to move your definition to the inner scope (here the while loop) if you place it outside and do not use it afterwards.
There should be no difference in speed of the 2 approaches. the compiler will optimize this for you. You can check the resultant IL code using Ildasm.exe.
The "best practice" is to use the smallest possible scope, i.e. code 2.
You shouldn't care about speed difference between these two. It will be extremely small (if any!).
Other differences? Second code makes number accessible after while loop, which may or may not what you want.
General rule: keep variables at the smallest possible scope, which means if you only need the variable within loop, declare it within loop. If you need it's value to be accessible when loop ends, declare it before loop.