question

ChristophRuchti-2844 avatar image
0 Votes"
ChristophRuchti-2844 asked ·

Class modules and public variables

I am in the process of writing may first class modules. I am intrigued with the concept of class modules as kind of LEGO bricks which communicate with the outside world through Let and Get properties. This looks like a very robust concept of programming! But what about Public variables? If I use inside a class module without intention a variable name, which exists as Public variable in my project, then I could end up with a mess. Is there a way to seal off my class module, may be with an option, such that it behaves like an island and ignores all public variables?

office-vba-dev
· 2
10 |1000 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.


I think that if a class has a local variable or a member, then it will use it instead of public variables.

What kind of public and class variables do you mean? Show an example.

0 Votes 0 ·

See my post below. A global public Var works this way:

If you don't qualify it with the module name - then the global existing one is used from the current module (if it exists). If not in current module, then the existing public one in OTHER modules is used.

If you have TWO public code modules with public var same name?

and you use the global var (outside of the above two modules), then

If you try to use it, then you get this as a compile error: "Ambiguous name detected"

So, at that point you have to go

debug.print Module1.CompanyName
and
debug.print Module2.CompanyName

And the above works 100% the SAME when you build a class in VBA.

Local closest scoped var is ALWAYS used - even when public.




0 Votes 0 ·
TvanStiphout avatar image
0 Votes"
TvanStiphout answered ·

Public variables are not needed inside a class. Declare them as Private.

·
10 |1000 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

AlbertKallal-4360 avatar image
0 Votes"
AlbertKallal-4360 answered ·

Well, public variables in your VBA code (not class) are in fact public.

but, when you use a class, then those public variables are public - but ONLY if you prefix the instance of the class.

In fact, MOST of my public members of a class OFTEN use public vars. The reason is simple:

Why write this:

 Option Compare Database
 Option Explicit
    
 Private m_CompanyName   As String
    
    
 Property Get CompanyName() As String
    
     CompanyName = m_CompanyName
        
 End Property
    
    
 Property Let CompanyName(v As String)
    
    m_CompanyName = v
       
 End Property

In above? You really don't need the m_CompanyName (the private member), and then the two code stubs (let/get) to set this up.

Why not just do this:

 Option Compare Database
 Option Explicit
    
 Public CompanyName      As String

Now in code you can do this:

 Sub MyTest()
    
    
    Dim MyCustomer As New clsCustomer
       
    MyCustomer.CompanyName = "IBM"
       
       
    
 End Sub

You still get inteli-sense like this:

79790-image.png

So any public variable you declare in that class as a base varibale name, and public? It will NOT be global your to your applcation and can ONLY be referenced by using a instance of that class, and also prefixing the name of the class in front.

And MORE interesting? in vb.net, and c#, this approach has quite much become the coding standard and norm now:

c#:

 class Person
 {
   public string Name  // property
   { get; set; }
 }

vb.net:

 Class Person
    
     Public Property Name As String
    
 End Class

Turns out this also works in VBA - so I see little if any need to write a getter + setter for what amounts to a variable that has no code attached to that method.

So yes, I freely use public vars in those classes. But they still are separated out to each instance of the class, and are not public (global) in scope. So you can do this:

    Dim Customer1 As New clsCustomer
    Customer1.CompanyName = "IBM"
       
    Dim Customer2 As New clsCustomer
    Customer2.CompanyName = "Microsoft"

So today, we see this trend both c# and vb.net, and I also been doing this in VBA for years. In fact, in most cases I don't even bother to write getters/setters for varibles. Only methods that have code do I write a actual get routine, and in fact in MOST cases I don't do that either.

So, for example, if we had something that returns company name and City? (lame example)

The you might see people write this:

 Option Compare Database
 Option Explicit
    
 '   CLASS customer
    
 Public CompanyName      As String
 Public City             As String
    
    
 Property Get CompanyAndCity() As String
    
    CompanyAndCity = CompanyName & "," & City
       
 End Property
    
    
 Function CompanyCity() As String
    
    CompanyCity = CompanyName & "," & City
    
 End Function
    
 Sub SetCompanytoUpperCase()
    
    CompanyName = UCase(CompanyName)
       
 End Sub


in above for this example - I wrote the Company + city as a public Property (that's the default - you don't need the word public in front), then I have a choice. "Function" or a Property Get.

In fact, any function (or sub - they don't return values) you expose becomes a method of that class.

You see this in inteli-sense - and note how it shows the function (and sub) as a public method with a different icon:

79818-image.png

So yes you are most free to declare and use public variables in that class - you NOT be able to use that variable without a instance of that class, and it is ONLY scoped to the instance of that class. And doing so as noted eliminates the need for getters/setters. I also do this in vb.net, and in c#? Well, you can do above, but you have to put in the get;set. (or leave the set out - it will be read only.

In fact, this is why I will sometimes use a public function to return a value - it is read only!

But, as a coding standard? Yes, you can freely declare public variables in your classes you create. They will not be global to the application, but only accessed by using a "instance" of that class.

Of Couse in a non class VBA module, then yes, public variables become global to all forms code and even your class instances you create in code. But this is NOT the case for public variables in those classes.

And thus in your class instance, if for some reason you DID need to use + consume a global var with the SAME name (and public)? You simply have to prefix that global (non class) variable with the code module's name. eg: Module1.CompanyName.

So, in that class instance, you always get the local scoped variable - even for public members. However, since you always have to create a instance of a class to use it? Then again, little issue exists as to which value/variable you get/use/have/work with in code.

I am rather comfortable recommending that you adopt public vars (members) as a coding practice when creating class objects in Access/VBA for public members - it will save lots of code and thus no need for getter/setters).


Regards,
Albert D. Kallal (Access MVP 2003-2017)
Edmonton, Alberta Canada






image.png (5.9 KiB)
image.png (14.9 KiB)
·
10 |1000 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

ChristophRuchti-2844 avatar image
0 Votes"
ChristophRuchti-2844 answered ·

Hi Albert

Many thanks for your interesting explanation! This use of public variables declared at class level appears to be an attractive concept avoiding unneccessary LET and GET procedures. I will consider it further.

I had another situation in mind where the public variable is declared at project level:


 ' module at project level
    
 Option Explicit
    
 Public Temp As Double
 Public Energy As Double
 Public Cells As Collection
    
 Sub test()
        
     Dim oCell As clsCell
        
     Set Cells = New Collection
        
     Temp = 13
     Set oCell = New clsCell
     oCell.p_Temp = Temp
     Cells.Add oCell
        
     Temp = 17
     Set oCell = New clsCell
     oCell.p_Temp = Temp
     Cells.Add oCell
        
     Debug.Print Cells(1).p_Energy
        
 End Sub
 '
 '------------------------------------------------------
 ' Class module
    
 Option Explicit
    
 Private m_Temp As Double
 Private m_Energy As Double
    
 Public Property Let p_Temp(T As Double)
     m_Temp = T
 End Property
    
 Public Property Get p_Energy() As Double
     ' code computing the energy, for simplicity take
     m_Energy = 2 * m_Temp
     p_Energy = m_Energy
 End Property

This works fine as long as I work with discipline and use the "m_" prefix for member variables. The printed result is 26. If I happen to forget a prefix as below

 Public Property Get p_Energy() As Double
     ' code computing the energy, for simplicity take
     m_Energy = 2 * Temp
     p_Energy = m_Energy
 End Property

then I get in trouble. The code works, but is producing an unexpected result (34). I am afraid of mistakes like this and was looking for a set-up, which simply prohibits such use of global variables in class modules.

Do you have a solution for this as well?

Kind regards

Christoph

· 2 ·
10 |1000 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

Global vars can be used anywhere and everywhere - that's the whole point.

I been on some dev teams in which globals were STRICT forbidden unless reviewed and agreed upon by the dev team. In other words?

To avoid problems with global vars in C#, VB6, VBA, vb.net, JavaScript, Pascal, c, c++ and insert every dev platform since the dawn of the computer industry?

If you want to avoid the problem, then you have to avoid global vars.

You can make a habit of using the "me." prefix as you code

So,

  me.Temp will not give you intel-sense, but me.m_Temp will.

But, as noted, the only way to avoid the issues with global vars is to only use such global vars in the absolute worst case in which no other choice having existing for that judgment call.

Global are considered "evil" in most developer circles - use them with caution and only when other choices don't exist.

I have no great answer - except to avoid.


Regards,
Albert Kallal
Edmonton, Alberta Canada.

0 Votes 0 ·

As a bit more FYI?
In that example (and yes, it was just a exmaple), one would declare the temp var in the first sub.

eg:
' module at project level

Option Explicit

Public Temp As Double
Public Energy As Double
Public Cells As Collection

The above 3 belong in that first code sub "test". Not scoped to the whole module. And say that 1 or two other subs in that example require the above vars? Then you PASS them from the first code stub that uses and sets the values - so that example does not make a case for module scoped variables - they should have been local to the first sub - not to the whole module. (again, I realize that was just an example). But, as noted, say a few other routines need those 3 values? You would pass them from the first routine on to the next one.

So in some case a wee bit of effort is required to use local vars - and in most cases with minimal efforts, you can achieve this. You be glad you did in the long run - trust me!!

0 Votes 0 ·
ChristophRuchti-2844 avatar image
0 Votes"
ChristophRuchti-2844 answered ·

Thanks. Knowing that a desired feature / option does not exist also helps. It provides the justifictaion for own efforts.

·
10 |1000 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.