PDA

View Full Version : Mix Constants and Enumerators



fredlo2010
10-15-2015, 07:54 AM
Hello,

I have a few modules in my project and they use a few global Constants and Enumerators. Sometimes these constants are part of the enumerators that just wrap them for easier reference. Some of them are in different modules because they enumerate a little bit specific.

My question here is should I create my modules in the following fashion:

Option1
============
- Module Enumerators
- Module Constants
- Module Logic

Option2
============
- Module Enumerators + Constants (note: it may contains constants that do not belong to enumerators)
- Module Logic

Option3
============
- Module Enumerators + Constants
- Module Some Enumerators + Module Logic


I know that perhaps there is no right answer to this. I just want to know how the community treats these. I am very tempted to use Option1 since I am building kind of a Framework for my business.

Any suggestions, ideas will be more than welcome :)

SamT
10-15-2015, 08:35 AM
Option 1 is the only right answer. There is a logical difference between Enumerators and Constants, the most important being that Enumerator can only be Whole Numbers and should always be closely related.

For instance

Enum SHT1Columns
sh1ID = 1
sh1FirstName = 2
sh1LastName = 3
End enum

Enum SHT2Columns
sh2ID = 1
sh2FirstName = 3
sh2LastName = 2
End Enum

Enum WCWarningColors
wcGoWithCaution = '(Index for yellow)
wcUseExtremeCaution = '(index for Orange)
wcALLSTOP = 3 '(Color Index for red)
End Enum

fredlo2010
10-15-2015, 08:46 AM
Hi Sam,

The only problem is when I have enumerators and I want to give them a specific number I have to declare it as a constant before otherwise the code will not compile. I am not sure what the message it but it happens from time to time. So I adopted the method of always creating a constant and then using them in the enumerator as this:



' -------------------------------
' Enumerator Connection Type
' -------------------------------
Private Const m_intMYSQL As Integer = 1
Private Const m_intSQL As Integer = 2
Private Const m_intSQL_QUERY_TABLE As Integer = 3


Enum enumConnectionType
eSQL = m_intSQL
eMySQL = m_intMYSQL
eSQL_QueryTable = m_intSQL_QUERY_TABLE
End Enum


Thanks. :)

SamT
10-15-2015, 03:53 PM
I have enumerators and I want to give them a specific number I have to declare it as a constant before otherwise the code will not compile. That's not right. Something else is going on. Did you notice in my example... I was very careful to not use any of Microsoft's standard prefixes, (xl, vb, wd, pj, et al.) I also followed MS's practice of using all caps in the Enum Name prefix and all LCase in the Enumerated Name's prefixes.

The purpose of Enums is to pass Meta information from one piece of code to another.

'Declare Public Enum enumConnectionType in Module "Globals"

'In another module
Private m_ConnectionType As Long

Sub1 ()
If Server = MainBranch then ConnectionType = eSQL
End Sub

Function ConnectionString() as String
Select Case ConnectionType
Case: eSQL
ConnectionString = "Blah, Blah, Blah"
End Select
End Function



' -------------------------------
' Enumerator Connection Type
' -------------------------------
Private Const m_intMYSQL As Integer = 1
Private Const m_intSQL As Integer = 2
Private Const m_intSQL_QUERY_TABLE As Integer = 3


Enum enumConnectionType
eSQL = m_intSQL
eMySQL = m_intMYSQL
eSQL_QueryTable = m_intSQL_QUERY_TABLE
End Enum

That structure is very bad practice. IF for any reason you can't use Enumerations, just use the Constants.

I often spend several minutes in my head reviewing various Enum prefixes and Names before I decide on one, because it's names should be specific and informative..

For instance I wrote a Concrete Estimator before I wrote the general Construction Estimator. For Enums in the Concrete estimator, I considered and rejected "ce" as a Prefix because I figured I would be using "ce" for the Construction Estimator. I wound up using "cn," "ca," and "cv;" "cn" for Globals, "ca" and "cv" for areas and volumes. This pattern allowed me to use "fr" for Globals, in the Framing Estimator, "fa" area and "fl" for linears.

If your Enums are Private to a Module, they don't even need a prefix because no other code can see them. You can then use the same name for Enums in different module with entirely different enumerated values. This does defeat the purpose of using Enums, because they are for Project wide use.

Using Enum enumConnectionType as an example, I would name it SQLConnectionType and us sqct as the enumerated names' prefix. For all SQL related Enums I would prefix the Enum Name with "SQ" and determine the Enumerated Names' prefix as sq + a mnemonic of the Enum Name. (SQLServer and sqsMainBranch)

fredlo2010
10-16-2015, 02:06 PM
Hi Sam,

Thanks a lot for your comments as helpful as always. I will remove the constants if I do not plan to use them. The only reason I was using the "enum" generic start for the name and the "e" for the elements was because my enumerators are not specific a single workbook but rather to a group of workbooks. If I need an enumerator I would just import them into my workbook.



Enum ENUMConnectionType
ectSQL = 1
ectMySQL = 2
ectSQL_QueryTable = 3
End Enum




Maybe this is something closer to the standards. :)

SamT
10-16-2015, 03:30 PM
SQLConnectionType is not specific to a workbook, but is specific to all SQLs. However, do the various SQL Languages have such Enumerated Constants?

Assuming that MySQL has a connection type Enum Named msqlCType, then you can make your Universal SQLConnectionType have an Enumerated Constant Named sqctMySQL = msqlCType.

IIRC, then if at the top of your Universal Enumerations Module, you put Option Private Module, it won't be compiled whenever you do compile another module. When you move it to another book and comment out or remove "Option Private Module," and compile it, the resulting error because there is no reference to MySQL, will remind you to make the Enum Project specific

Paul_Hossler
10-16-2015, 03:57 PM
I used Const's for things that won't change, or if/when they do I only need to update one statement.

I use Enum's to take advantage of Intellesense and to easily constrain choices but especially to make code readable

Public, Private, Option Private are scope issues and work like any other scope-able thing

In Demo the calls to ShirtColor are (to me at least) much more readable that the calls to ShirtColor2, especially since intelisense gave me the enumerated values to pick

You can add a prefix (e.g. szSmall) if you want to. After all, Microsoft uses xl.... and mso... as prefixes for their enumerations




Option Explicit
Public Const MaxShirtSize As Long = 18
Private Const MinShirtSize As Long = 5

Public Enum ShirtSize
SmallShirt = MinShirtSize
MediumShirt = 10
LargeShirt = 16
ExtraLargeShirt = MaxShirtSize
End Enum

Sub Demo()
Call ShirtColor(SmallShirt) 'Intellesense works
Call ShirtColor(MediumShirt)
Call ShirtColor(LargeShirt)
Call ShirtColor(ExtraLargeShirt)
' Call ShirtColor(SomeOtherSizeShirt) Variable not defined at compile time

Call ShirtColor2(5)
Call ShirtColor2(10)
Call ShirtColor2(16)
Call ShirtColor2(18)
Call ShirtColor2(14)

End Sub

Private Sub ShirtColor(TheSize As ShirtSize)

Select Case TheSize
Case SmallShirt
MsgBox "Only comes in Red"
Case MediumShirt
MsgBox "Only comes in Green"
Case LargeShirt
MsgBox "Only comes in Blue"
Case ExtraLargeShirt
MsgBox "Only comes in Pink"
End Select
End Sub


Private Sub ShirtColor2(TheSize As Long)

Select Case TheSize
Case 5
MsgBox "Only comes in Red"
Case 10
MsgBox "Only comes in Green"
Case 16
MsgBox "Only comes in Blue"
Case 18
MsgBox "Only comes in Pink"
Case Else
MsgBox "Shirts only come in Size 5, 10, 16, and 18"
End Select
End Sub






Just my 2 cents

SamT
10-16-2015, 06:07 PM
I just want to know how the community treats these. I am very tempted to use Option1 since I am building kind of a Framework for my business.

Would this be your personal business or the company you work for. My advice would be different in both cases, and would depend on what you personal business was. I will be assuming that it is Program Development until I hear otherwise :D.

I guess one way of putting it is: Constants rarely change values, Enums never change values. Looking at Paul's post: If he did not assign values to the ShirtSize Enum and just let VBA auto assign values from 1 to 4, all his code, except ShirtColor2 would still work. In fact you can design an entire shipping department around that code and never once assign an actual size value anywhere.

Paul_Hossler
10-17-2015, 05:56 AM
If he did not assign values to the ShirtSize Enum and just let VBA auto assign values from 1 to 4, all his code, except ShirtColor2 would still work. In fact you can design an entire shipping department around that code and never once assign an actual size value anywhere.

True. Only used explicit values to tie in the use of Const within the Enum, since that was something the OP mentioned. Although it is possible to manipulate the value of the Enum if needed, but I don't do that.




ShirtPrice = 1.5 * TheSize



The most useful things about using Enum IMVHO are

1) Improved readability of the code
2) Controlling the range of values
3) Making use of VBA's Intellisense (screen shot)

SamT
10-17-2015, 10:00 AM
I agree, but I would place Controlling the range of values as third in your list, even if it is the most used reason.

snb
10-18-2015, 06:10 AM
@PH


Public Enum ShirtSize
SmallShirt = 5
MediumShirt = 10
LargeShirt = 16
ExtraLargeShirt = 18
End Enum

Sub M_Demo()
M_ShirtColor LargeShirt
End Sub

Private Sub M_ShirtColor(TheSize As ShirtSize)
MsgBox TheSize
End Sub

- Intellisense works without 'call' as well
- no matter how you declare 'theSize', it will be passed as numeric.

Paul_Hossler
10-18-2015, 07:06 AM
- Intellisense works without 'call' as well
- no matter how you declare 'theSize', it will be passed as numeric.


1. Yes, but an Enum will still give the rest of us a 'human' not 'computer' value to pick from in both cases since you do need the call with more than one parameter. My (very) personal choice is to be consistent and use Call even if there's only one



Option Explicit

Public Enum ShirtSize
Smallshirt = 5
MediumShirt = 10
LargeShirt = 16
ExtraLargeShirt = 18
End Enum

Sub M_Demo()
Call M_ShirtColor(LargeShirt, Smallshirt)
End Sub

Private Sub M_ShirtColor(TheSize As ShirtSize, Backup As ShirtSize)
MsgBox TheSize
MsgBox Backup
End Sub






2. Also yes, but unless you want to do math on a Enum, I don't think it really matters. If you do need to do math, then you might have the wrong type of variable


3. I always go by Fowler and KISS for my wordy code and I try to make it self documenting to the most practical extent:



“Any fool can write code that a computer can understand. Good programmers write code that humans can understand.”

-- Martin Fowler, "Refactoring: Improving the Design of Existing Code"


In six months when I have to go back to it, I need all the help I can get

Paul_Hossler
10-18-2015, 07:07 AM
I agree, but I would place Controlling the range of values as third in your list, even if it is the most used reason.

I probably should not have numbered them since IMVHO I think they are all just as important

snb
10-18-2015, 08:37 AM
When I have seen intellisense for the first argument my short time memory hasn't deteriorated that speedily that I need it for the second one.

Primarily I write code for a computer, secondly for myself (if I have to amend it - which I should heve prevented in the coding itself in the first place -).
So programming consistently (name-conventions, use /naming of variables) is the most important. 'Normal' language is more ambiguous than the use of consistent programming conventions You can see that in VBA where 'deleting', 'removing', 'removeitem', 'clearing','is nothing' basically do the same thing but have a lot af different 'names'. If you want to be consistent keep far from 'normal' language. That's why E=mc2 and a^2+b^2=c^2 have such a long/international life.

Paul_Hossler
10-19-2015, 12:24 PM
When I have seen intellisense for the first argument my short time memory hasn't deteriorated that speedily that I need it for the second one.


The example sub just used parameters of the same type. The point was just to demo Intellisense. Could just as easily been ...



Private Sub M_Clothing(SizeShirt As ShirtSize, SizePants As PantSize, BeltSize as Long, PaidBy as PaymentMethod, SalesClerk as Emp, Discount as Double, Returned as Boolean)


each with their Enums





'Normal' language is more ambiguous than the use of consistent programming conventions


Agreed, but I don't think that's the same as self-documenting code

I could have filled the macro with things like c00 = ... and c01 = ..... and in six months I would have to figure it all out again.

fredlo2010
10-27-2015, 01:55 PM
Hi everyone,

Thanks to all of you for the discussion and the ideas. I apologize for not responding but I did not get any notifications :( I though this was a dead topic and my busy schedule sometimes makes me forget to come back and check.


Would this be your personal business or the company you work for.[...] Program Development until I hear otherwise :D.

Good one Sam, I am so sorry this is just English been my second language acting up :)

The arguments are very interesting an cover what I use enumerators for. To improve readability and limit the number of my choices. Thanks a lot for your inputs, always greatly appreciated.

As I commented before I will refactor my code to something similar to this one. I am really falling in love with it.



Enum enumConnectionType
ectSQL
ectMySQL
ectSQL_QueryTable = 3
End Enum


Thanks once again :)