Naming global variables in a structured way - vba

My current project has global constants that define certain rows and columns in workbooks that this project will be searching through. I have defined them as such:
Public Const headRow As Integer = 1
Public Const descRow As Integer = 2
Public Const pnumCol As Integer = 1
Public Const teamCol As Integer = 2
Public Const dateCol As Integer = 3
Public Const hourCol As Integer = 4
Public Const typeCol As Integer = 5
Public Const taskCol As Integer = 6
Public Const noteCol As Integer = 7
I'm wondering if there is a cleaner way to define these that would allow me to write these in a way such as:
ColumnNums.team
ColumnNums.task
ColumnNums.note 'etc
I think something similar to this could be done by defining my own type, but that would probably not be worthwhile. I'm basically wanting this to be an easy way to remember the variable names as I write more code, as well as to be able to count how many items I have in each group. Would a Type or Collection be useful in this case?

For mixed variable types, you can put it in a class module, name the class module ColumnNumbers and put the following code in:
Public Property Get Team() As Long
Team = 1
End Property
Public Property Get TeamName() As String
TeamName = "Team One! :-)"
End Property
Then you can use it in any module like this:
Dim colNums As New ColumnNumbers
Sub foo()
MsgBox colNums.Team
End Sub
If you only want to return long values, put it in an enum:
Enum ColumnNumbers
Team = 1
Description = 2
End Enum
Sub foo()
MsgBox ColumnNumbers.Team
End Sub
Chip pearson has already done a fantastic job of describing enums here it's worth a read if you have yet to discover them.

You could use public arrays like this:
Public ColumnNum(0 To 2) As Long
Public RowNum(0 To 2) As Long
Used together with an enum:
Public Enum Category
team
task
note 'etc.
End Enum
Then things like ColumnNum(team) will function like a public variable:
Sub test1()
ColumnNum(team) = 5
End Sub
Sub test2()
Debug.Print ColumnNum(team)
End Sub
If these two subs are run in order than 5 is printed.

Related

VB NET Object Oriented

I'm a little new to classes on VB.NET (and to whole OOP concept in general) so sorry in advance for my poor explanation.
I've created a class like so:
Public Class MyApp
private var1 as integer = 2
Private Function getProfile(id As Integer)
'Imaginary server request according to ID
'Following that was received:
Dim name As String = "John"
Dim age As integer = 30
End Function
End Class
I want to be able to call getProfile by using myApp.getProfile and that's something I can handle.
What I can't manage is displaying only the age or name.
Something like this:
MyApp.getProfile(4341).age
How can I achieve something like this? Like having sub-functions in a function.
To call the method that way it needs to be static (marked Shared) and public. To make it return the name and age as properties you need a class with those properties. Example:
Public Class MyApp
Public Shared Function GetProfile(id As Integer)
Dim name As String = "John"
Dim age As integer = 30
return New ServerResult(name, age)
End Function
End Class
Public Class ServerResult
Public Name as String
Public Age as Integer
Public Sub New(n as String, a as Integer)
Name = n
age = a
End Sub
End Class
Usage example:
Dim age as Integer = MyApp.GetProfile(42).Age
Another:
Dim result As ServerResult = MyApp.GetProfile(1337)
Dim info As String = String.Format("{0}, {1}", result.Name, result.Age)
Note: Making the method static is based on how you wanted to call it. You might want to create an instance of the MyApp class instead, and have a method that is not static.

How do I declare a constant variable with a value of a function call

In a VBA module I have the following declaration of constants:
Private Const const_abc = 3000
Private Const const_def = 900
Private Const const_etc = 42
' and so on and so forth
Now, I have to initialize these values with a one time function call, ideally something like so
Private Const const_abc = someFunc(18)
Private Const const_def = someFunc( 7)
Private Const const_etc = someFunc( 5)
' and so on and so forth
Of course, this won't work in VBA. So, is there a common pattern on how to deal with such a requirement?
I probably could go like so
Private const_abc As Double
Private const_def As Double
Private const_etc As Double
sub initConsts()
const_abc = someFunc(18)
const_def = someFunc( 7)
const_etc = someFunc( 5)
end sub
But then I'd have to make sure that initConsts is called which I'd rather not do.
Edit As per the question of S O, I am using MS-Access.
Create a class that reads the cell and presents a Get-only interface to the value.
Here's a class called ItsMyValueClass
Option Explicit
Private pMyVal As Integer
Public Property Get MyValue() As Integer
MyValue = pMyVal
End Property
Private Sub class_initialize()
'pMyVal = Sheet.Range("somewhere)
pMyVal = 17
End Sub
And here's the code in your module:
Option Explicit
Sub IsItReadOnly()
Dim valu As ItsMyValueClass
Dim x As Integer
Set valu = New ItsMyValueClass
x = valu.MyValue
'valu.MyValue = 23 'compile error "Can't assign to read-only property"
End Sub
Public Function White() as Long
White = RGB(255,255,255)
End function
Private Sub TestIt()
Debug.Print "White is " & White
White = 123 ' <-- compile error
End Sub
in a one-liner that works with modules and classes alike for pure constant-like access:
Public Property Get myConst() As Integer: myConst = 3: End Property
you would use it like this:
Sub test()
Debug.Print "myConst: " & myConst 'would print: "myConst: 3"
End Sub
and if it has to be initialized with a custom value once, one could do it with a static property and one or many private variables:
Private ci As Boolean 'constants initialized
Private myConst1_ As Integer
Private myConst2_ As Integer
Static Property Get myConst1() As Integer
If Not ci Then init
myConst1 = myConst1_
End Property
Static Property Get myConst2() As Integer
If Not ci Then init
myConst2 = myConst2_
End Property
Private Sub init()
'these can come from anywhere:
myConst1_ = 3
myConst2_ = 5
ci = True
End Sub
they are initialized on the first access of the first "constant" property
if you have to initialize them earlier one could just call the init function earlier (and optionally remove the ci variable and all related lines if it is ensured that the properties are not accessed earlier)

Performance Enum vs class?

i found few new style (for me) to "define" output from select query.
Private Enum Item
ID
Item
Description
End Enum
Private Class Item
Private ID as String
Private Item as String
Private Desc as String
End Class
I 'm thinking of using either one of them. by using class i does not need to re-cast the element type before i display. but Enum seems like easier to understand.
Anyone have some suggestion how to decide?
Enum members are numeric (usually integer, but can be long). But they are not variable and do not change at runtime. So your enum equates to:
Private Enum Item
ID = 0
Item = 1
Description = 2
End Enum
If you want Description to be a string, then a class is a better idea. Enums are used to reference or index something or limit/define a selection. Like:
Public Property Stooge As Stooges
Friend Enum Stooges
Larry
Moe
Curly
Shemp
CurlyJoe
End Enum
The Stooge Property must be one of those values. in code it will show you the text ("moe") but store and integer (1). users will be shown the text in drop downs etc.
You can associate a description with Enum constants:
Public Enum Stooges
<Description("Larry - Funny one")> Larry
<Description("Moe - 'Smart' One")> Moe
<Description("Curly - Sore One")> Curly
<Description("Shemp - One with bad haircut")> Shemp
<Description("CurlyJoe - Last one")> CurlyJoe
End Enum
To get the description for a single one:
Public Shared Function GetDescription(ByVal EnumConstant As [Enum]) As String
Dim fi As Reflection.FieldInfo =
EnumConstant.GetType().GetField(EnumConstant.ToString())
Dim attr() As DescriptionAttribute =
DirectCast(fi.GetCustomAttributes(GetType(DescriptionAttribute),
False), DescriptionAttribute())
If attr.Length > 0 Then
Return attr(0).Description
Else
Return EnumConstant.ToString() ' return enum name if no Descr
End If
End Function
Usage: str = enumHelper.GetDescription(Stooge.Moe) (enumHelper is the name of the calss where the static/shared function resides).
To get a String Array of all the descriptions:
Public Shared Function GetDescriptions(ByVal type As Type) As String()
Dim n As Integer = 0
Dim enumValues As Array
Try
enumValues = [Enum].GetValues(type)
Dim Descr(enumValues.Length - 1) As String
For Each value As [Enum] In enumValues
Descr(n) = GetDescription(value)
n += 1
Next
Return Descr
Catch ex As Exception
MessageBox.Show(ex.Message)
Return Nothing
End Try
End Function
Usage: Dim strEnum As String() = enumHelper.GetDescriptions(GetType(Stooges))
From your question, what you really mean is Struct vs Class. I would default to creating a class. The main reason to use a struct vs a class, is when you need value semantics -- assignment/parameters copies the bits, not a pointer. This is fairly rare in my experience. Unless you have a compelling reason (and you know the difference), go with a class.

Instance variables and local variables confusion

Please take a look at sample1 below:
Public Class LocalVariable
Public Sub Run()
Dim TestVariable As Integer
TestVariable = Method1(TestVariable)
TestVariable = Method2(TestVariable)
TestVariable = Method3(TestVariable)
End Sub
Private Function Method1(ByVal x As Integer) As Integer
Return x + 1
End Function
Private Function Method2(ByVal x As Integer) As Integer
Return x + 2
End Function
Private Function Method3(ByVal x As Integer) As Integer
Return x + 3
End Function
End Class
and sample 2 below:
Public Class InstanceVariable
Dim TestVariable As Integer
Public Sub Run()
Method1()
Method2()
Method3()
End Sub
Private Sub Method1()
TestVariable = TestVariable + 1
End Sub
Private Sub Method2()
TestVariable = TestVariable + 2
End Sub
Private Sub Method3()
TestVariable = TestVariable + 3
End Sub
End Class
The outcome is obviously the same after each program runs i.e. TestVariable=6. Every example I find online and at work uses sample 1. Surely this is a misuse of instance variable as TestVariable should be shared across functions? Therefore an instance variable should be used.
The two samples don't mean the same thing.
The difference is what happens if you call Run() more than once over the life of the program. The Run() method in sample 2 never resets TestVariable, so it will continue to get larger and larger. In sample 1, the result will always be 6 because TestVariable is a new variable with each call to the function. Which is more correct depends entirely on what you're trying to do.
There is a third option
All else being equal, I also recommend the sample 1 approach from those two options. However, instance vs local variable is not the distinction. There's no reason sample 1 couldn't also use an instance variable with those method definitions. So our third option would look like this:
Public Class InstanceVariableWithSampleOneFunctions
Dim TestVariable As Integer
Public Sub Run()
TestVariable = Method1(TestVariable)
TestVariable = Method2(TestVariable)
TestVariable = Method3(TestVariable)
End Sub
Private Function Method1(ByVal x As Integer) As Integer
Return x + 1
End Function
Private Function Method2(ByVal x As Integer) As Integer
Return x + 2
End Function
Private Function Method3(ByVal x As Integer) As Integer
Return x + 3
End Function
End Class
This uses the instance variable from sample 2 with the methods from sample 1. I'll call it sample 3.
This cuts better to the heart of your question, because now sample 3 has the same behavior as sample 2. Whether you should choose 1 or 2 depends on which behavior you need. But whether you should choose 2 or 3 depends on the merits of the coding style. Both 2 and 3 rely on an instance variable in the Run() method, but 2 also uses an instance variable in the additional methods, while 3 uses a local variable.
I can say that at this point, comparing 2 and 3, I definitely prefer sample 3. The methods from sample 3 have more of a functional style: accept an input, return an output. This gives them a higher level of abstraction, which makes it easier to refactor sample 3 to do things like move those methods elsewhere... say, to a utility class where one set of methods can be shared with both samples 1 and 3. Since you mentioned threading, typically this style makes it easier, not harder, to do multi-threading correctly.
One concrete example how this method style is better is that it's composable. This attribute allows me to re-write sample 3's Run() method like this and be confident of getting the same results:
Public Sub Run()
TestVariable = Method3(Method2(Method1(TestVariable)))
End Sub

How to accumulate the sum in VB?

Dim index As Integer
Dim choice As String
Dim total As Integer
total = 0
index = NumericUpDown1.Value
Dim arr(4) As Integer
arr(0) = 10
arr(1) = 5
arr(2) = 21
arr(3) = 33
If index > 0 Then
choice = (Combobox1.SelectedItem.ToString + " x " + NumericUpDown1.Value.ToString)
ListBox1.Items.Add(choice)
CheckedListBox1.Items.Add(choice)
total += arr(Combobox1.SelectedIndex) * index
TotalLabel.Text = total.ToString()
Else
MsgBox("error.")
End If
I can calculate the total of single choice, but fail to accumulate to sum.
What's wrong of the code?
Current Situation:
Step 1:
choose arr(0), index = 2
total = 20
Step 2:
choose arr(2), index = 1
total = 21
Correct Situation:
Step 1:
choose arr(0), index = 2
total = 20
Step 2:
choose arr(2), index = 1
total = 41
You'll need a either a global variable or a class with a public variable. You should create a Transaction class to store the data about the transaction and probably a Product class to store the data about the product. What you put in it is up to you, but I'd start out with something like this:
Public Class Transaction
Private _productsList As List(of Product)
Private _transationNumber As Integer
'...more stuff...
'you'll want to remember what products are in your "cart" for the transaction
Public Property ProductsList As List(of Product)
'your get/set accessors
End Property
Public Property TransactionNumber As Integer
'your get/set accessors
End Property
Public Property TotalTransactionCost() As Double
Get
'this will sum of the prices of all of the products you have stored in your
'list of products for this transaction
Return _productsList.Sum(product => product.Price)
End Get
End Property
Public Sub New()
'...constructor stuff
End Sub
Public Sub AddProductToTransaction(byval product)
_productsList.Add(product)
End Sub
End Class
Public Class Product
Private _price As Double
Private _productName As String
Private _UPC As String
Public Property Price() As Double
'your get/set accessors
End Property
Public Property ProductName() As String
'your get/set accessors
End Property
Public UPC As String () As String
'your get/set accessors
End Property
Public Sub New()
'constructor stuff
End Sub
End Class
These are a couple class shells to get you started. If you're serious about making a product, this is a step in the right direction. If you're going to write code, write it the right way.
If you're just looking for a quick and dirty solution, you can declare a global variable and just keep a running sum. Just don't forget to clear it out before you start a new transaction.
You'll want to do something like:
Private TransactionCost As Double in your form outside of all your methods.
Again, I would recommend the first way of going about things. You'll need at least those two classes and they'll definitely be more fleshed out for a real product.
I hope this helps and answers your question. If it does, hit me with an upvote and accept the answer. Welcome to SO.