Code Focused

A Lifetime of Data in C# and Visual Basic

Or, how lifetime can mean different lengths of time, depending on whether it's procedural- or block-level.

Data variables in C# and Visual Basic normally define themselves by the amount and type of data they hold. The string type, with its ability to store 2 billion characters, is often seen gloating about its capacity. But as with humans, variables are also defined by another key aspect: lifetime.

Lifetime indicates how long a variable remains available, from the time it first appears in memory until the moment it's purged or garbage collected. Normally, a variable lives as long as its container lives: Class-level variables live and die when the class that contains them starts and ends; local variables in procedures magically pop into existence when the procedure begins, and disappear when the program says goodbye to the method's or property's code.

Visual Basic and C# endow their variables with the same lifetimes, with one key exception: block-level variables. These local variables, when defined within a block statement, behave differently between the languages, depending on how they're declared. Consider the identical chunks of C# and Visual Basic code in Listing 1.

Listing 1: Examples of C# and Visual Basic Local Variables
// ----- C# example.
List<int> result = new List<int>();
for (int counter = 1; counter <= 5; counter++)
{
  if ((counter % 2) == 1)
  {
    int trackingData = default(int);
    trackingData += 1;
    result.Add(trackingData);
  }
}
MessageBox.Show(string.Join(",", result));

// ----- Visual Basic example.
Dim result As New List(Of Integer)
For counter As Integer = 1 To 5
  If ((counter Mod 2) = 1) Then
    Dim trackingData As Integer = Nothing
    trackingData += 1
    result.Add(trackingData)
  End If
Next counter
MessageBox.Show(String.Join(",", result))

Both blocks pass through the loop five times, enter the conditional If block three times and generate the same output, the text "1,1,1." The trackingData variable is declared at the block level within each language, appearing within the body of the If statement. Each time the loop triggers entry into the If block, the trackingData variable is reinitialized, incremented, and added to the result list. It's as if the trackingData variable was brought into existence anew on each processing of the conditional If block. That is, the trackingData variable exhibits block-level lifetime.

However, it's all a deception, at least in Visual Basic. The Microsoft .NET Framework allows each language to implement lifetime rules as it requires, and Visual Basic and C# define different rules: C# sticks with the block-level lifetime shown in the sample, but Visual Basic implements procedure-level lifetime for block-level variables, even if that expanded lifetime isn't apparent.

Procedure-level lifetime means that a variable comes into existence when the procedure begins, even if it's declared inside of a subordinate block within the procedure. The sample in Listing 1 hid this fact by reinitializing trackingData each time through the block. In this case, it's initialized to Nothing, which fills the variable with the default value for an integer, zero:

Dim trackingData As Integer = Nothing

Unlike in C#, Visual Basic doesn't require that local variables be initialized before use. In the absence of such initialization, Visual Basic sets local variables to their default values, as if they were assigned Nothing. However, this only happens when the procedure begins, not each time the Dim statement is encountered during processing. Let's remove the explicit initialization of the trackingData variable:

If ((counter Mod 2) = 1) Then
  ' ----- Initialization to Nothing removed.
  Dim trackingData As Integer
  trackingData += 1
  result.Add(trackingData)
End If

The trackingData variable is still initialized to Nothing, but only once, upon entering the full procedure. Now, when you run the entire block of code, it generates "1,2,3" instead of three identical digits. Even though the variable is defined within a block and can only be used within that block, its value from the previous time in the block is remembered and reused when the block is entered again. Block-level lifetime rules and initialization requirements prevent this from happening in C#.

About the Author

Tim Patrick has spent more than thirty years as a software architect and developer. His two most recent books on .NET development -- Start-to-Finish Visual C# 2015, and Start-to-Finish Visual Basic 2015 -- are available from http://owanipress.com. He blogs regularly at http://wellreadman.com.

comments powered by Disqus

Featured

Subscribe on YouTube