Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 5 years ago.
Improve this question
Trying to create a universal placeholder for all methods in my class instead of having to declare multiple varibles per class, else declaring multiple instances of a class and using its methods can be quite costly
dim beginIndex, endIndex as integer
public sub GetLastFiveLetters(str as string)
' assume string is 10 characters long
beginIndex = 5
endIndex = 10
ResetVariable() 'I want beginIndex and endIndex to be 5 and 10 after I call this method
return = str.substring(5, 10)
end sub
public sub GetFirstFiveLetters(str as string)
'assume string is 10 characters long
beginIndex = 0
endIndex = 5
ResetVariable() 'I want beginIndex and endIndex to be 0 and 5 after I call this method
return = str.substring(0, 5)
end sub
public sub ResetVariables()
beginIndex = 0
endIndex = 0
end sub
The reset variable method is simply there for example purposes, what i want to do is be able to use a variable with multiple values across multiple methods...
So when i call reset variable, even though im technically reseting the variable across all methods, i want to variables to retain their method specific values ... so in the first method even though i called the reset method, i want beginIndex to still be 5 and endIndex to still be 10, it is only in the resetvariable method where beginIndex will be 0 and endIndex will be 0
Regarding storing variables in methods:
... declaring multiple instances of a class and using its methods can be quite costly
WRONG. Variables declared in methods, i.e. local variables, exist only while the method is being executed. On the other hand, variables declared at the class level, i.e. fields, exist for each class instance or object for its the whole life time.
You must make a distinction between the variable declaration and the memory that a variable uses at runtime. The variable declaration itself is not compiled into code. Only the variable accesses (i.e. setting or reading the variable) are. At runtime, local variables start to exist when a method is called and cease to exist after the method returns.
Class fields, however, exist as long as the objects exist, whether a method is being called or not.
Related
I'm stuck in VBA and I couldn't find a good answer in the other questions related to error 91. I want to create an object and store variables and arrays inside that object. I tried an approach like I would do in js:
Dim block As Object
...
Set block = Nothing
block.Name = "Unbekannter Prüfblock"
block.Awf = "Unbekannter Anwendungsfall"
block.Conditions = Array()
block.Checks = Array()
I use the "Set block = Nothing" because I will use it multiple times in a loop.
But all I get is error 91 - Object variable not set
How can I set the object?
Do I really have to declare everything in vba?
Isn't there a "stop annoying me with declaration notices" toggle? ;-)
Update
Thank you all so much for the detailed answers!
As suggested I created a class for "block" and also a class for "condition" and "check". Block for example:
Option Explicit
Public name As String
Public awf As String
Public conditions As Collection
Public checks As Collection
Then I use it inside my code like this:
Dim bl As Block
Dim co As Condition
Dim ce As Check
Set bl = New Block
bl.name = ws.Range("B" & i).value
bl.awf = ws.Range("B" & i).value
Set co = New Condition
co.attr = ws.Range("B" & i).value
co.value = ws.Range("C" & i).value
bl.conditions.Add co
VBA isn't Javascript; objects and their members cannot be created inline, they need a class definition.
When you make a member call against an object, you refer to it by name, and whenever that name refers to a null reference (Nothing) you'll get error 91.
To fix it, you need to ensure every member call is made against a valid object reference. Using the Set keyword you can assign such a reference, and to create a new instance of an object you can use the New keyword followed by the name of the class that defines the type you want a new instance of:
Dim Block As Object
Block.Something = 42 ' Error 91
Set Block = New SomeClass ' set reference
Block.Something = 42 ' OK
Note that because the object is declared As Object, every member call is late-bound (resolved at run-time); if the member doesn't exist (or if there's a typo), you'll get error 438 at run-time.
You can move this error to compile-time with early binding by using a more specific data type for the declaration:
Dim Block As SomeClass
Because the members of SomeClass are known at compile-time, the IDE will now provide you with a member completion list when you type up a member call, and typos will no longer be valid at compile-time: strive to remain in the early-bound realm whenever possible! Note: As Variant (explicit or not) is also similarly late-bound.
So we add a new class module and call it SomeClass and we add a couple of public fields:
Option Explicit
Public Name As String
Public Case As String
Public Condition As Variant
Public Check As Variant
And now you can create and consume a new instance of that class, and add instances of it to a collection to process later (note: you can't do that with a UDT/Type).
The VBIDE settings have an annoying option ("automatic syntax check", IIRC) that immediately pops a message box whenever there's a compilation error on the current line; uncheck it (invalid lines will appear in red, without a message box), but do have the "require variable declaration" setting checked: it will add Option Explicit to every module, and that will spare you from a number of easily avoidable run-time errors, moving them to compile-time.
In JS, you can add properties (together with values) on the fly to an object. That's not possible in VBA (and most other languages).
Your declaration Dim block As Object is defining a variable that is supposed to point to an Object. But it isn't pointing to anything yet, per default it is initialized with Nothing, which is, literally, nothing, and has neither properties nor methods, it's just a placeholder to signal "I don't point to anything yet". Furthermore, Object cannot be instantiated.
in VBA, you assign something to an object variable with Set (this is different to most other languages). If you want to create a new object, you use the keyword New.
However, before you do that, you need to know what kind of object (which class) you need. This can be an existing class, eg a Worksheet or a Range in Excel, or it can be an own defined class (by creating a new class module in your code). In any case, you need to define the properties and the methods of that class. Considering the most simple class module Class1 (of course you should think about a more usefull name):
Option Explicit
Public name as String
Public case as String
(I will not start to talk about private members and getter and setter).
You then write
Dim block As Class1
Set block = New Class1
block.name = "Unbekannter Prüfblock"
(But block.data = "Hello world" will not be possible as data is not a member of your class.)
Big advantage of this attempt is that the compiler can show you when you for example mistyped a property name before you even start your code (Debug->Compile). In JS, you will get either a runtime error or a new property on the fly - hard to find nasty bugs.
If you later need a new (empty) object, just create a new object using New. If the old object is not referenced anywhere else, the VBA runtime will take care that the memory is freed (so no need to write Set block = Nothing).
In case you really don't know the properties in advance (eg when you read XML or JSON files or a key-value list from an Excel sheet...), you can consider to use a Collection or a Dictionary. Plenty of examples on SO and elsewhere.
One remark to block.Condition = Array(). Arrays in VBA are not really dynamic, you cannot add or remove entries easily during runtime. In VBA, you have static and dynamic arrays:
Dim a(1 to 10) as String ' Static array with 10 members (at compile time)
Dim b() as String ' Dynamic array.
However, for dynamic members you need to define how many members you need before you write something into it, you use the ReDim statement for that. Useful if you can calculate the number of members in advance:
Redim b(1 to maxNames) ' Dynamic array, now with maxNames members (whatever maxNames is)
You can change the array size of a dynamic array with Redim Preserve, but that should be an exception (bad programming style, inperformant). Without Preserve, you will get a new array, but the former data is lost.
Redim Preserve b(1 to maxNames+10) ' Now with 10 more members.
If you really don't know the number of members and it can change often during runtime, again a Collection or a Dictionary can be the better alternative. Note that for example a Dictionary can itself a Dictionary as value, which allows to define Tree structures.
Regarding your issue adding to the collection:
You need to add this code to your class module "Block" - only then you can add objects to the collections
Private Sub Class_Initialize()
Set conditions = New Collection
set checks = new Collection
End Sub
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!!
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 7 years ago.
Improve this question
Can someone explain why I would do this code this way?
I Have a small grasp of delegates but don't understand the advantages
Can some one try and help explain what I am doing here. I am very new to classes and delegates. Just need some help.
Here is my delegate code
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click, Button2.Click
Dim NewCompare As New Compare
NewCompare.Num1 = 3
NewCompare.Num2 = 2
NewCompare.CheckCompare(AddressOf GreaterThan)
NewCompare.CheckCompare(AddressOf SmallerThan)
End Sub
Function GreaterThan(Num1 As Integer, Num2 As Integer) As Boolean
Return Num1 > Num2
End Function
Function SmallerThan(Num1 As Integer, Num2 As Integer) As Boolean
Return Num1 < Num2
End Function
End Class
Class Compare
Property Num1 As Integer
Property Num2 As Integer
Delegate Function CompareValues(Num1 As Integer, Num2 As Integer) As Boolean
Public Sub CheckCompare(compare As CompareValues)
If compare(_Num1, _Num2) Then
MsgBox(_Num1)
Else
MsgBox(_Num2)
End If
End Sub
End Class
Well, "the essential idea" of "delegation" is simply: "identify Someone Else that you can ask."
In this example, the Compare class exists to "compare two objects." But you've said that it is to delegate that responsibility to some other function that is not a part of its own definition. Furthermore, you specify exactly what an acceptable "delegate function" must look like.
The Delegate Function declaration specifies what that delegate must look like. "An acceptable delegate must return a Boolean, and it must accept (exactly ...) two Integer parameters." The language ensures, at compile-time, that this is so.
In your two, separate, calls to the CheckCompare method of the Compare class, you provide separate references to two functions, GreaterThan and LessThan, both of which are conformant to the strictures set out in the Delegate Function declaration. Therefore, the language permits them to be used in the calls. And, as instructed, the CheckCompare method invokes the delegate-function that it has been given, when told to do so.
It does not know (but, it does not care) exactly which delegate it is. The language ensures that "an acceptable delegate" has been provided, and all this method needs to do, is to call "it."
this code is the same as others it is a random between 1 and 4 yet for some reason it says it is being used before it has a value it is the same code as 3 others that are the same but with different names yet this is happening can someone please help me?
Dim npc As Random
Dim ndamage As Integer
ndamage = npc.Next(1, 4)
If (Playerhealth.Value - ndamage) <= 0 Then
Playerhealth.Value = 0
Else
Playerhealth.Value = Playerhealth.Value - ndamage
End If
In the first three lines of code,
Dim npc As Random
Dim ndamage As Integer
ndamage = npc.Next(1, 4)
you declare npc and use it before it is assigned a value. You should use New to create a new instance:
Dim npc As New Random
Further Explanation
Random is a class, which means that its default value is Nothing (also called null in C#), so before it can be used it needs to be assigned a value. The easiest way in this case is to use New directly in the variable declaration line.
Random is a Class which provides a lot of methods to get different random numbers.
To access these methods you have to create an object (sometimes called instance) of that class.
This is done by the new operator. This operator will allocate new space on the heap (which is a memory area) and will fill it with objects values and references to methods and other objects.
If you skip the new statement, you program tries to access to not allocated memory. In several languages this will end up in an nullpointer exception, in vb.net you get an used before it has assigned value exception.
To solve your problem, create an object of the random class:
Dim npc As New Random
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.