A Safer Way to Use TempVars

I'll start by saying that it is my OPINION, clearly indicated as such, that this particular use of TempVars seems like a bad approach to me. When you write non-class code that deals with global values that persist across multiple iterations, there is a potential for a situation in which your variables can be changed outside of the code for which those variables exist. We call that a side-effect and it is generally not considered to be a good idea.

A class object is supposed to be like a black box. When you create a class, the whole point of a property Let or property Get or property Set routine is that you want to set class variables that are protected (or if you prefer, isolated) within the body of the class object. When using a TempVar as the place where you keep your object properties, you can easily bypass those protections, in effect cheating your way around the sides of the black box. With careful attention to the issue, you can of course avoid causing a problem, but I think that here, we have in essence a dangerous short-cut that invites side-effect meddling.

If you say "Oh, this makes it easier to define the object" ... I don't disagree. It may, indeed, be EASIER to populate arbitrary properties when you use a TempVars method for storing object properties. However, remember that a TempVars variable can only be string or numeric. If the class object can contain an object then the TempVars container cannot contain it. Granted, the amount of work needed to set up that class object's internal data might be tedious - but that is a price you pay for the "black box" effect that is part of class objects.

Then there is the issue of whether you would wish to implement two instances of the object. In a traditional class object, no problem. You just create a new instance of the object which contains everything you need. But a TempVars isn't stored with the class object so now you would have to either avoid two objects or have more complex code to use alternate names for the 2nd (or later) TempVars "container."

Again, an OPINION as to why TempVars for class object properties might represent a problem. And I understand that some folks might disagree with a "hard line" stand here.
 
Is it because of PredeclaredID attribute = False attribute?
Member variables can also be reset with PredeclaredID = True in the event of an unhandled error or "End" call.

Test with predeclared class:
Code:
Private Sub Class_Initialize()
   Debug.Print "Class_Initialize"
End Sub
+
call (without error handling):
Code:
public sub RaiseAnError()
    dim x as long
   x = 1 / 0
end Sub
 
Last edited:
I'll start by saying that it is my OPINION, clearly indicated as such, that this particular use of TempVars seems like a bad approach to me. When you write non-class code that deals with global values that persist across multiple iterations, there is a potential for a situation in which your variables can be changed outside of the code for which those variables exist. We call that a side-effect and it is generally not considered to be a good idea.

A class object is supposed to be like a black box. When you create a class, the whole point of a property Let or property Get or property Set routine is that you want to set class variables that are protected within the body of the class object. When using a TempVar as the place where you keep your object properties, you can easily bypass those protections, in effect cheating your way around the sides of the black box. With careful attention to the issue, you can of course avoid causing a problem, but I think that here, we have in essence a dangerous short-cut that invites side-effect meddling.

If you say "Oh, this makes it easier to define the object" ... I don't disagree. It may, indeed, be EASIER to populate arbitrary properties when you use a TempVars method for storing object properties. However, remember that a TempVars variable can only be string or numeric. If the class object can contain an object then the TempVars container cannot contain it. Granted, the amount of work needed to set up that class object's internal data might be tedious - but that is a price you pay for the "black box" effect that is part of class objects.

Then there is the issue of whether you would wish to implement two instances of the object. In a traditional class object, no problem. You just create a new instance of the object which contains everything you need. But a TempVars isn't stored with the class object so now you would have to either avoid two objects or have more complex code to use alternate names for the 2nd (or later) TempVars "container."

Again, an OPINION as to why TempVars for class object properties might represent a problem. And I understand that some folks might disagree with a "hard line" stand here.
Nope, Tempvars are variants. In fact, I use separate calls for each datatype for tempvars for that reason, in which I explicitly type them using the appropriate datatype when setting them.

The point here is that the code in question restores the original value defined in the table for the TempVars, does it not?

With regard to the complaint that a TempVars value can be changed outside the original definition. I suppose a careless coder could do that, but it would require an explicit statement to do that, which is contrary to the whole point of this article. In other words, you go to the trouble of creating a table for the defined TempVars to be used, then create the classes, but then turn around and redefine one of them elsewhere? Possible, but not a logical thing, for the most part.

I know there are pros and cons to TempVars. One is free to use them or not.
 
Member variables can also be reset with PredeclaredID = True in the event of an unhandled error or "End" call.
Well, Now I’m completely confused. Let me take back a step.

Because the global variables will reset, we use tempvars.
To make tempvars more convenient, it’s suggested to use a class.
If member variables in a class can also be reset, why using the class at all?
 
No, there are no setters and the values are in a USysTable that is delivered in each front end. I see no reason to empty and reload it. It carries things like the value of a twip, the name of the application, the version number, contact info for the developer supporting it, etc. Those things are a mix of values unique to that application and those values, like the twip, for which a constant is useful.
OK, that is a different use then. Probably different then most people use for TempVars, I would think. Those are more like application settings / constants. Not really "Temporary" as the name implies.
 
If member variables in a class can also be reset, why using the class at all?
The entire (predeclared) class is reset in the event of an unhandled error => Class_Initialize is triggered
However, this only occurs if there are unhandled errors, which should not exist at runtime for the user. ;)

I think you have to differentiate between what you need these properties for.
Are they application-wide settings that you want to read from tables and keep in memory or are they just short-term values for the transport of form A to be used in form B or query Y?
 
Well, Now I’m completely confused. Let me take back a step.

Because the global variables will reset, we use tempvars.
To make tempvars more convenient, it’s suggested to use a class.
If member variables in a class can also be reset, why using the class at all?
Remember they do not have any class variables. All values are stored in TempVars which stay alive for the duration of the session.
In their method if you had a runtime error
TV.Payment would still have a value. Here is why

1. You start your application
2. a global instance of TV is created.
3. You assign a value
TV.Payment
it Creates TempVar("Payment")

4. You throw an error here
I assume the original global TV terminates.
But the Tempvars live on
5. You restart your code and a new global TV is created.
And since tempvars never died then
debug.print TV.Payment works still even though an instance of TV terminated and then was created.

Remember in the authors example there are No class variables, and if you want this to persist then the tempvar way is how to do it.
 
OK, that is a different use then. Probably different then most people use for TempVars, I would think. Those are more like application settings / constants. Not really "Temporary" as the name implies.
Correct. "TempVars" as someone already noted, is an unfortunate name, and as I suggested, it survived from Macros in Access Web Apps (gone, but notforgotten).
 
The entire (predeclared) class is reset in the event of an unhandled error => Class_Initialize is triggered
However, this only occurs if there are unhandled errors, which should not exist at runtime for the user. ;)

I think you have to differentiate between what you need these properties for.
Are they application-wide settings that you want to read from tables and keep in memory or are they just short-term values for the transport of form A to be used in form B or query Y?
TempVars themselves can fill either role. I believe it's safe to say the NoLongerSet article focused on the former.

I use them in both ways, in some applications. Keeping in mind that I retired years ago and these apps are those that I have for personal use only.
 
Are they application-wide settings that you want to read from tables and keep in memory or are they just short-term values for the transport of form A to be used in form B or query Y?
Both. 8 application width and 22 short term (if my memory serves me right)
 
Nope, Tempvars are variants. In fact, I use separate calls for each datatype for tempvars for that reason, in which I explicitly type them using the appropriate datatype when setting them.

The point here is that the code in question restores the original value defined in the table for the TempVars, does it not?

With regard to the complaint that a TempVars value can be changed outside the original definition. I suppose a careless coder could do that, but it would require an explicit statement to do that, which is contrary to the whole point of this article. In other words, you go to the trouble of creating a table for the defined TempVars to be used, then create the classes, but then turn around and redefine one of them elsewhere? Possible, but not a logical thing, for the most part.

I know there are pros and cons to TempVars. One is free to use them or not.
Here are the datatyping Functions I use to handle the variant problem:
Code:
Option Compare Database
Option Explicit


Public Function TempVarsBoolean(ByRef VarName As String) As Boolean   
10        TempVarsBoolean = Nz(TempVars(VarName), False)     
End Function

Public Function TempVarsDate(ByVal VarName As String) As Date
20        If IsDate(TempVars(VarName)) Then
30            TempVarsDate = DateValue(Nz(TempVars(VarName), #1/1/2099#))
40        Else
50            TempVarsDate = #1/1/2099#
60        End If
End Function

Public Function TempVarsLong(ByVal VarName As String) As Long
70        TempVarsLong = CLng(Nz(TempVars(VarName), 0))
End Function

Public Function TempVarsString(ByVal VarName As String) As String
80        TempVarsString = CStr(Nz(TempVars(VarName), "*"))
End Function

varName is the name of the TempVar passed into the function to ensure it is typed for use in other functions.
 
Remember they do not have any class variables. All values are stored in TempVars which stay alive for the duration of the session.
In their method if you had a runtime error
TV.Payment would still have a value. Here is why

1. You start your application
2. a global instance of TV is created.
3. You assign a value
TV.Payment
it Creates TempVar("Payment")

4. You throw an error here
I assume the original global TV terminates.
But the Tempvars live on
5. You restart your code and a new global TV is created.
And since tempvars never died then
debug.print TV.Payment works still even though an instance of TV terminated and then was created.

Remember in the authors example there are No class variables, and if you want this to persist then the tempvar way is how to do it.
Million thanks.
now I get the whole picture.
 
Note that TempVars values can also be changed externally.
Code:
Dim app As Access.Application
Set app = GetObject("Y:\our\path\to\FE.accde")
app.TempVars("xyz").Value = ...
 
The property procedure does not necessarily have to be in a class, it can also be in a standard module.

I use custom properties all the time to store/handle variables. However there is one problem using properties. If you try and set them in the form before you open the Form, they are unavailable to the form events, load, open, etc. This is easily fixed by writing your own "load event" as a public function and then calling that.
 
Million thanks.
now I get the whole picture.
The fact that the life of Tempvars extends beyond an unhandled runtime error is that really useful?
I am not sure how I would use that in an application to any benefit. If I distribute an application with unhandled errors I got bigger problems.
I would be interested in a real world example of using this as a benefit.

With that said you can easily write a class without this table automation that gives the benefits of
Visibility
Data type checks
And a function to use these values in a query.
Just add the class variables.

Or again you could modify George's example to add Setters and still persist your values while having ability to read and write.
 
Nope, Tempvars are variants.

Here I go again, believing what Microsoft documentation says.


The description of the "value" argument to the TempVars.Add method says:

The value to store as a TempVar. This value must be a string expression or a numeric expression. Setting this argument to an object data type will result in a run-time error.

IF you can use other data types, great... but that isn't what is advertised. I like to avoid stretching the boundaries on things.
 
IF you can use other data types, great... but that isn't what is advertised. I like to avoid stretching the boundaries on thing
This would be an advantage of writing your own "TempVar" class that is not a wrapper for Tempvars. You could then hold objects.
 
This would be an advantage of writing your own "TempVar" class that is not a wrapper for Tempvars. You could then hold objects.

Good point. In the context of this discussion, though, all that would do is move the work from pre-defining all properties of the class object to re-inventing a wheel for the TempVars container object to fully contain your dynamic properties in the under-defined object. I see that as a "pay me now, pay me later" scenario.
 
Because the global variables will reset, we use tempvars.
They only reset when you have unhandled errors. It is better practice to fix the unhandled errors.

I don't like TempVars for a couple of reasons.
1. They are variants and I prefer strong typing
2. People define them hither, thither, and yon. When I use them, which is rare, I have a separate module where I define ALL of my global variables and TempVars.
3. You have to reference them using .Value which seems awkward.

If you read about good programming techniques, you will find that global variables are not recommended in general for a number of reasons that I won't go in to. I've been using Access for as long as it has been around and programming for a lot longer, so I had to solve this problem in my own Access applications 25+ years ago. What I did instead of using global variables was to create an unbound form that opens hidden when the application opens. It has a control for each variable I need that needs to be referenced by multiple procedures and for some reason, I don't want to/can't pass around easily.

Controls are also variants but you can use formatting to get Access to help you out and ensure that dates are dates and numbers are numbers. Turns out that even large applications require very few global variables if you mind the "good programming techniques". I rarely have more than a dozen. I make the form when I start development, even before I know if I will even need global variables because that way, I don't have to think about it. The fewer decisions like this I have to make, the smoother development goes and I don't find myself having to rework code because it got "bigger" than I initially thought.

The extra advantage of this technique is that during testing, I can make the form visible so I can easily watch values change and I can even help with testing to change the value on the fly to make the code take a different path so it becomes easier to test multiple scenarios.
 
Good point. In the context of this discussion, though, all that would do is move the work from pre-defining all properties of the class object to re-inventing a wheel for the TempVars container object to fully contain your dynamic properties in the under-defined object. I see that as a "pay me now, pay me later" scenario
Not exactly.
As I mentioned you could modify the authors approach.
1. Still have a table with properties and data types.
2. Use this table to create/recreate the the properties in the class
3. Use the table data to create local class variables the same way it creates the properties

If I get time I will see if I can demo, it is actually a pretty interesting idea. Then you can mimic Tempvar that have
visibility,
data types
and support objects
 

Users who are viewing this thread

Back
Top Bottom