Calling variable across subs

  • Thread starter Thread starter Jeff
  • Start date Start date
J

Jeff

Still new to vb.net in VS2005 web developer...

What is the proper/standard way of doing the following - setting the value
of a variable in one sub and
calling it from another? E.g., as below. The code below draws an error as
indicated.
Surely there has to be a better way than to make xxx a session variable?

Thanks

Jeff

Sub test()
Dim xxx As Integer = 9
End Sub

Sub Test3()
label1.text = xxx <- x is not declared
End Sub
 
You should call it as a Function.

Public Function xxx() as Integer

Dim i as Integer = 9

Return i

End Function

Sub Test3

label1.text = xxx

End Sub
 
You are attempting to access a variable that is out of scope. Scope has
three major categories:

1. Local: Persisted in the current sub routine you are in
2. Module: Persisted in the current class or module you are in
3. Global: Persisted throughout the application

There are finer deviations of setting scope for a variable, lets take a look
at the following code snippet

public module MainModule

'Make a global variable
global itemIteratorCount as integer

'Make a module variable
private sampleString as string

shared sub Main

'Make a local variable
dim iLoop as integer

for i as integer = 1 to 100
for j as integer = 1 to 100
'Take note that i is only available for the scope of this loop
iloop += iloop
itemIteratorCount +=1
makeString()
console.writeline sampleString
next j
'Take note that j is not avaiable, it has gone out of scope until the
next i
next i

end sub

private function makeString() as string
'i is not available here
'j is not available here
'iloop is not available here

'sampleString is available here
sampleString = System.Guid.NewGuid.ToString("N")

'itemIterator is available here
itemIteratorCount +=1

end function


End Module


Having said all of this, no one really wants us to use GLOBAL any more.
Instead you should set up a singleton instead to host global variables.

example:

public class GlobalValues
private shared pItemIterator as integer
shared public readonly property ItemIterator as Integer
get
return pItemIterator
end get
set (value as integer)
pItemIterator = value
end set
end property
end class

to access the itemiterator you simply call:

GlobalValues.ItemIterator +=1

Application and Session values have their own scope and purpose as well.
There is also a psuedo scope known as caching.

Application values allow everyone user session to interact with shared
information

Session values are like the global or singleton values

Caching is a hybrid and can be used at either the Application or Session
level. Ideally, caching is used to minimize the load and roundtrips to the
backend resorces such as database and file servers.
 
Earl said:
You should call it as a Function.

Public Function xxx() as Integer

Dim i as Integer = 9

Return i

End Function

Sub Test3

label1.text = xxx

End Sub

Thanks (both Amdrit and Earl). Reference the above, if I use the method
above and access xxx several times from several different subs and if the
value I'm placing into the variable is taken from a cell in a database, will
the database be accessed each time or is there some type of caching involved
where the db access actually occurs once?

In other words, is the above still a good strategy if instead of the simple
example: "Dim i as Integer = 9" i is assigned after a number of commands
that read from a database and then xxx is access multiple times from several
subs.

Jeff
 
If I understand your question properly, this is a major reason variables
exist. They hold onto information in memory for as long as you need it.

Typically, you will want to minimize impact on your database. To do so, you
would read the data into variables, then work with the data, and finally
commit any changes back to the database.

Here is some sample code for you to browse and understand a little better.
This is a high stripped down set of code, and is not intended to be the only
approach when working with data. (I appologize to everyone on slow
connections, the code example is kinda long.)

Public Class SomeForm
'psuedo class to represent a form
' only the require portions of the form
' code logic are represented.

'Assume a datagrid to list multiple rows of data
' two text boxes to be bound to a single instance of data
' a button to save the data

Private Sub BindDataList()

Dim objList As System.Collections.Generic.List(Of GlobalData.myData)


'Clean up our datagrid, incase are refreshing the list
Me.dbgData.dataSource = Nothing
Me.dbgData.TableStyles.Clear()

'Read the data in
objList = GlobalData.Datalist("somefilter")

'Bind it to our datagrid
Me.dbgData.dataSource = objList

'Optionally set up tablestyles for the datagrid
'Code ommitted

End Sub

Private Sub BindDataItem()

'This would be called each time a row is clicked on the datagrid
Dim objDataList As System.Collections.Generic.List(Of
GlobalData.myData)
Dim objData As GlobalData.myData


'Clear any existing bindings that may be present
Me.txtValue1.databindings.clear()
Me.txtValue2.databindings.clear()

'Get the list the datagrid is bound to
objDataList = CType(Me.dbgData.datasource, ListBox(Of mydata))

'Get the selected row
objData = CType(objDataList(Me.dbgData.CurrentRowIndex),
GlobalData.myData)

'Set this row to the current row
GlobalData.LoadData(objData)

Dim b As DataBinding

'bind value1
b = New DataBinding("Text", objData, "Value1")
Me.txtValue1.Databindings.Add(b)

'bind value2
b = New DataBinding("Text", objData, "Value2")
Me.txtValue2.Databindings.Add(b)

End Sub

Private Sub btnSave_Click(ByVal sender, ByVal e) Handles btnSave.Click
GlobalData.SaveData()
End Sub

End Class
Public Class GlobalData
Private Shared pstrConnectionString As String
Private Shared pobjCurrentData As myData

Shared Sub New()
'Initialize our connectionstring
pstrConnectionString = "ConnectionInfoHere"
End Sub

''' <summary>
''' Load on piece of data into memory
''' </summary>
''' <param name="filter"></param>
''' <remarks>The filter would ideally reference a unique column in the
database to return one row of data.</remarks>
Public Shared Sub LoadData(ByVal filter As String)
'Using filter information, load your data
'My example will just hardcode the values
pobjCurrentData = New myData
With pobjCurrentData
.Value1 = "Value1"
.Value2 = "Value2"
End With

End Sub

''' <summary>
''' Load one piece of data into memory
''' </summary>
''' <param name="dataObject"></param>
''' <remarks>Use this method if you have a list of data myData
objects.</remarks>
Public Shared Sub LoadData(ByVal dataObject As myData)
pobjCurrentData = dataObject
End Sub

''' <summary>
''' This is the target row of data that we are working with.
''' </summary>
''' <value></value>
''' <remarks></remarks>
Public Shared ReadOnly Property CurrentData() As myData
Get
Return pobjCurrentData
End Get
End Property

''' <summary>
''' Here we would save the data back to the database
''' </summary>
''' <remarks></remarks>
Public Shared Sub SaveData()
'Check if there are changes to worry about
If CurrentData.HasChanges Then
'Save the data
' database call is ommitted here
End If
End Sub

''' <summary>
''' Unload the current data from memory
''' </summary>
''' <remarks>Bound object may still have a reference to the data. That
is ok, since the only way to save it is in the SaveDataMethod</remarks>
Public Shared Sub UnloadData()
pobjCurrentData = Nothing
End Sub

''' <summary>
''' Simulate loading multiple rows of data from the database.
''' </summary>
''' <param name="filter"></param>
''' <returns></returns>
Public Shared Function Datalist(ByVal filter As String) As
System.Collections.Generic.List(Of myData)
'again, I will simulate loading from the database
Dim objReturn As System.Collections.Generic.List(Of myData)
objReturn = New System.Collections.Generic.List(Of myData)

For i As Integer = 1 To 25
Dim objMyData As myData
objMyData = New myData

With objMyData
.Value1 = System.Guid.NewGuid.ToString("N")
.Value2 = System.Guid.NewGuid.ToString("N")
End With

objReturn.Add(objMyData)

Next

Return objReturn

End Function

''' <summary>
''' This class represents one row of data
''' </summary>
''' <remarks></remarks>
Public Class myData

'Data values read in from the database
Private pobjDatabaseValue1 As String
Private pobjDatabaseValue2 As String

'Working set of data, a copy if you will
Private pobjCurrentValue1 As String
Private pobjCurrentValue2 As String

'Flags to determine is the data is null
Private pblnIsNullValue1 As Boolean = True
Private pblnIsNullValue2 As Boolean = True

Public Property Value1() As String
Get
'Test if our value is null, if so throw an exception
If pblnIsNullValue1 Then Throw New
System.Data.DataException("Value1 is Null")

'Otherwise return the value
Return pobjCurrentValue1
End Get
Set(ByVal value As String)
pobjCurrentValue1 = value
pblnIsNullValue1 = False
End Set
End Property

Public Property Value2() As String
Get
'Test if our value is null, if so throw an exception
If pblnIsNullValue2 Then Throw New
System.Data.DataException("Value2 is Null")

'Otherwise return the value
Return pobjCurrentValue2
End Get
Set(ByVal value As String)
pobjCurrentValue2 = value
pblnIsNullValue2 = False
End Set
End Property

Public ReadOnly Property IsValue1Null() As Boolean
Get
Return pblnIsNullValue1
End Get
End Property

Public ReadOnly Property IsValue2Null() As Boolean
Get
Return pblnIsNullValue2
End Get
End Property

Public Sub SetValue1Null()
Value1 = String.Empty
pblnIsNullValue1 = True
End Sub

Public Sub SetValue2Null()
Value2 = String.Empty
pblnIsNullValue2 = True
End Sub

Public ReadOnly Property HasChanges() As Boolean
Get
If pobjCurrentValue1.CompareTo(pobjDatabaseValue1) <> 0 Then
Return True
If pobjCurrentValue2.CompareTo(pobjDatabaseValue2) <> 0 Then
Return True
End Get
End Property

End Class

End Class
 
Yup, not too many people would use such a function with a "magic" number.
They would instead do as your intuition says, have the function pull the
value from a database, or perhaps the function would do some calculations
(maybe even based upon some other functions, which in turn might pull their
values from a database). Indeed the idea of reusability is right there at
the top of the heap when it comes to writing code, so ideally your funciton
would get maximum possible use from throughout your app.

But it sounds to me like you are interested in a function that caches the
data. The simplest way to handle it would be to widen your scope on the
variable used to contain the data locally. For example, if your function
pulled some number from the database, you might set a Private variable that
can be accessed within the class where it is needed. For more complex data
"holding" operations, you might use various containers such as datasets and
datatables.

Private TestInteger as Integer

'this Function might be in a separate class or the Module
Public Function xxx() as Integer

Dim i as Integer = GetFancyCalcFromDb()
Return i

End Function

Sub SetVariableToHoldState()

'now TestInteger will hold the state of xxx without the need to call it
again
'you might actually set this the first time you call it
TestInteger = xxx()

End Sub

Sub Test3

label1.text = TestInteger

End Sub

Sub Test4

label200.text = TestInteger

End Sub
 
But it sounds to me like you are interested in a function that caches the
data. The simplest way to handle it would be to widen your scope on the
variable used to contain the data locally. For example, if your function
pulled some number from the database, you might set a Private variable


Ahh, that's exactly what I needed. I kept reading about the different
designations of scope and I skipped over 'private' because the name lead me
to believe that it was irrelevant to what I was after.

You understood perfectly. That solved the problem - and it was so easy.

Thanks

Jeff
 
After awhile you'll start thinking like this. It's sick, really sick ...
"now if I can widen the scope on my marketing ..." Glad the kickstart
helped.
 
Back
Top