Finally, Value versus Reference Objects


At this point, you have encountered the term "value" several times. I have covered value objects, value types, value this, and value that. Are you ready to look under the hood at these value "things"? All right, here we go.

As you know, all classes in .NET are derived from System.Object. This, of course, includes the System.ValueType class. If you view the System.ValueType class using the Class Viewer tool, you will see the output shown in Listing 9-4. (The line numbers in Listing 9-4 have been added manually.)

Listing 9-4: Class Viewer Output of the System.ValueType Class
start example
 (1) // from module 'c:\winnt\microsoft.net\framework\v1.0.3705\mscorlib.dll'  (2)  public abstract class ValueType : 
 (3)    object (4)    // Constructors (5)    // Methods  (6)  public virtual bool Equals(object obj); (7)    public virtual int GetHashCode(); (8)    public Type GetType(); (9)    public virtual string ToString(); (10) // end of System.ValueType 
end example
 

Let's take a closer look at each displayed line in Listing 9-4. Starting with line 1, you can see that the ValueType class is physically located in the same module (i.e., mscorlib.dll) as the System.Object base class. In line 2, the word "abstract" is used. This is where value type objects begin their departure from the System.Object class. The "abstract" term is C# (and C++) lingo for MustInherit . MustInherit is VB .NET lingo for a class that can't be instantiated directly but can be inherited.

You will notice that line 3 states the class that the ValueType class is derived from. Then, on line 4, you see that there are no constructors shown. Although there is actually a constructor, there is not a Public constructor. The System.ValueType constructor is defined as Protected . This is a more restrictive setting than Public. A Protected method (including the methods that have the name NEW, as in the constructor) can only be called from within the local class (or a derived class).

Note  

The System.Object class is (ultimately) the base class for all objects. The System.ValueType class is (ultimately) the base class for all value type objects. Generally , the former are said to be reference type objects and are allocated on the heap, and the latter are said to be value type objects and are allocated on the stack. Loosely speaking, people will use the term "reference objects" or simply "objects" to refer to reference type objects. Value type objects are loosely referred to as "value types" or simply "types."

Lines 6 through 9 of Listing 9-4 should look familiar. Recall that these methods were also in System.Object. Therefore, this is a good example of inheritance in action. Because System.ValueType is derived from the base class System.Object, it is able to inherit those four methods. This type of inheritance is provided (or restricted) according to the definition (visibility and accessibility) of the method found in the base class. The derived class (in this case) is able to either use the inherited method as-is or use one of several object-oriented techniques to change the behavior of the exposed method.

Putting the System.ValueType Class into Perspective

The System.ValueType class uses the object-oriented technique of overriding for the inherited methods Equals, GetHashCode, and ToString. The GetType method is simply inherited. Through overriding, the ValueType class is able to implement logic that is more appropriate for a value type object.

For now, let me offer a few more value type facts to you (if you do not mind):

  • Once declared, value type objects simply "exist" on the stack.

  • Value types do not require declaration and instantiation ”just declaration.

  • It is not necessary to explicitly call the constructor on a value type.

  • The default (parameterless) constructor that is implicitly called for value type objects simply initializes them.

  • You can optionally create and call and constructors that have been added to a value type (i.e., overloaded constructors).

Just in case you were wondering, the variables that you have grown accustomed to creating in the Working-Storage Section of your COBOL .NET program are value types. These include the variables that look like the legacy COBOL data types (using PICTURE clauses and so on) you are familiar with. In Chapter 6, these data types were referred to as the COBOL .NET data types that provided intrinsic support for .NET data types.

Note  

In your legacy mainframe COBOL development, when you used pointers and address registers to refer to memory allocations , you were closely mimicking the type of behavior of .NET's reference type objects.

Well, that is about it as far as contrasting the two types of objects. Are you now wondering what this "value versus reference" issue looks like in real life? Good! Let's continue.

Objects: Use with Care

Let's say, just for example, that you are creating a collection of data items. You stumble across the .NET class System.Collections.ArrayList and decide to use it. In some cases, this is a great idea. But in other cases, this could prove to be disastrous. Sometimes it may be better to choose the basic System.Array class over the ArrayList class. But why?

Note  

These days, on the mainframe, the term "array" has become synonymous with the term "table." Both terms represent data items defined with the COBOL OCCURS clause. Legacy mainframe COBOL programmers will recall that we generally used arrays to store data to be used as output. A table, on the other hand, was typically used for lookup functionality. In hindsight, this was simply an exercise in semantics. After all, arrays and tables looked the same, acted the same, and were defined and stored the same. The Array and ArrayList classes discussed here, on .NET, are functionally similar to the arrays and tables used in mainframe COBOL development.

Well, the elements of the ArrayList class have the generic data type of Object (remember the System.Object class). The Array class, on the other hand, allows you to use specific types (i.e., Integer, Boolean, and so forth). Although you could even choose Object as the type for your Array class, ideally you should try to avoid this. The ArrayList, however, does not give you this choice. This becomes critical if the collection of data items that you want to store (in your Array or ArrayList class) happens to be a collection of value type data items (do you remember the discussion about boxing in Chapter 8?).

Naturally, this does not mean that you should always avoid the ArrayList class. It does mean that you should put some thought into the data types you choose, the classes you use, and how you use them.

Are you convinced that this topic is important? Good! Because you appreciate the significance of this topic, I have created two sets of sample applications that will illustrate object type choice and object type usage. I believe you will find the code in the following two sections rather interesting.

Object Type Sample Code

When you create your managed .NET applications, you might want to know how normal, everyday programming code can potentially affect performance. Eventually, most good developers get around to observing the performance of their applications. On the other hand, a great developer (like yourself) will have had this question of performance already in mind, simply as a habit. Thus, performance of the reference type object and the value type object is the focus in the sample applications that follow.

The first set of sample applications (StringBuilderExampleVB and StringBuilderExampleCobol) demonstrates the difference between using the new .NET StringBuilder class for string concatenation versus the more traditional string concatenation approaches.

COBOL .NET Example

The sample application in Listing 9-5 makes use of the System.Environment class. Observe the technique of using the TickCount property that is exposed by the System.Environment class. This technique may be useful to you for other coding scenarios. Now, let's explore the code sample.

Listing 9-5: The StringBuilderExampleCobol Project
start example
 000010 IDENTIFICATION DIVISION. 000020* This is an example of how traditional String 000030* Concatenation differs from using the  000040* .NET StringBuilder Class for Concatenation. 000050 PROGRAM-ID. MAIN. 000060 ENVIRONMENT DIVISION. 000070 CONFIGURATION SECTION. 000080 REPOSITORY. 000090* .NET Framework Classes 000100    CLASS SYS-ENVIRONMENT_Obj AS "System.Environment" 000110    CLASS SYS-STRINGBUILDER AS "System.Text.StringBuilder" 000120    PROPERTY PROP-TickCount AS "TickCount". 000130* 000140 DATA DIVISION. 000150 WORKING-STORAGE SECTION. 000160   77 sb OBJECT REFERENCE SYS-STRINGBUILDER. 000170   77 sb1 OBJECT REFERENCE SYS-STRINGBUILDER. 000180   77 i PIC S9(9) COMP-5. 000190   77 ibeg PIC S9(9) COMP-5. 000200   77 myStartTick PIC S9(9) COMP-5. 000210   77 myFinishTick PIC S9(9) COMP-5. 000220   77 myComputedTick PIC S9(9) COMP-5. 000230   77 myDisplayTick PIC Z(9). 000240   77 myString1 PIC X(430000). 000250   01 NULL-X PIC X(1). 000260 LINKAGE SECTION. 000270 000280 PROCEDURE DIVISION. 000290  000300    DISPLAY "Begin String Builder Example." 000310    DISPLAY " " 000320  000330* Start logic for String (FUNCTION) Compare  000340    SET myStartTick to PROP-TickCount of SYS-ENVIRONMENT_Obj. 000350    MOVE "I am creating .NET Garbage to be Collected" TO myString1 000360    MOVE 50 to ibeg 000370    PERFORM VARYING i  000380      FROM 0 BY 1 UNTIL i >= 9999 000390       STRING " * One Piece of Garbage to be collected * "  000400             DELIMITED BY SIZE 000410             INTO myString1 000420       WITH POINTER ibeg 000430    END-STRING 000440    END-PERFORM 000450    SET myFinishTick to PROP-TickCount of SYS-ENVIRONMENT_Obj. 000460  000470    DISPLAY "Milliseconds for traditional String concatenation: " 000480    COMPUTE myComputedTick = (myFinishTick - myStartTick) 000490    MOVE myComputedTick to myDisplayTick 000500    DISPLAY myDisplayTick 000510  000520* Start logic for StringBuilder Compare using Default size of 16 000530    SET myStartTick to PROP-TickCount of SYS-ENVIRONMENT_Obj. 000540    INVOKE SYS-STRINGBUILDER "NEW" 000550       RETURNING sb. 000560    INVOKE sb "Append" 000570       USING BY VALUE "I am creating .NET Garbage to be Collected : " 000580       RETURNING sb. 000590 000600    PERFORM VARYING i  000610       FROM 0 BY 1 UNTIL i >= 9999 000620          INVOKE sb "Append" 000630           USING BY VALUE " * One Piece of Garbage to be collected * " 000640           RETURNING sb 000650    END-PERFORM 000660    SET myFinishTick to PROP-TickCount of SYS-ENVIRONMENT_Obj. 000670  000680    DISPLAY "Milliseconds for StringBuilder - using default Size: " 000690    COMPUTE myComputedTick = (myFinishTick - myStartTick) 000700    MOVE myComputedTick to myDisplayTick 000710    DISPLAY myDisplayTick 000720 000730* Start logic for StringBuilder(500) Compare 000740    SET myStartTick to PROP-TickCount of SYS-ENVIRONMENT_Obj. 000750    INVOKE SYS-STRINGBUILDER "NEW" 000760       USING BY VALUE 500 000770       RETURNING sb1. 000780    INVOKE sb1 "Append" 000790       USING BY VALUE "I am creating .NET Garbage to be Collected : " 000800       RETURNING sb1. 000810 000820 PERFORM VARYING i  000830       FROM 0 BY 1 UNTIL i >= 9999 000840          INVOKE sb1 "Append" 000850             USING BY VALUE " * One Piece of Garbage to be collected * " 000860             RETURNING sb1 000870    END-PERFORM 000880    SET myFinishTick to PROP-TickCount of SYS-ENVIRONMENT_Obj. 000890  000900    DISPLAY "Milliseconds for StringBuilder - initialized Size : " 000910    COMPUTE myComputedTick = (myFinishTick - myStartTick) 000920    MOVE myComputedTick to myDisplayTick 000930    DISPLAY myDisplayTick 000940  000950    SET SB TO NULL 000960    SET SB1 TO NULL 000970 000980    DISPLAY "Enter X and Press Enter to Exit.". 000990    ACCEPT NULL-X.  001000 001010 END PROGRAM MAIN. 
end example
 

Please take a moment to review the sample code in Listing 9-5. In this sample project, StringBuilderExampleCobol, you will notice that I used the traditional COBOL STRING function first. Then, for comparison, I included two instances of the new .NET System.Text.StringBuilder class. Notice the inclusion of constructors for the StringBuilder class. After you run the program, the console window will display the output lines shown in Listing 9-6.

Listing 9-6: Console Display Output of the StringBuilderExampleCobol Project
start example
 Begin String Builder Example.     Milliseconds for traditional String concatenation:         40 Milliseconds for StringBuilder - using default Size:        220 Milliseconds for StringBuilder - initialized Size :        161 Enter X and Press Enter to Exit. 
end example
 
Note  

The displayed "millisecond" results (shown in several demonstration code samples) should be used for relative comparisons. The actual values will likely vary each time you run them on your own computer. In fact, the relative comparison of the "millisecond" results from one computer as compared to those of a different computer could vary as well. Depending on the any number of variables (i.e., the type of processor, amount of memory, type of hard disk, and so on), results should and will vary. The resulting numbers displayed here are for demonstration purposes only.

Interesting! In this COBOL .NET example, the traditional string concatenation approach (using the native COBOL STRING function) is actually faster than the .NET Framework StringBuilder class. I have to admit that this was a bit surprising. Nevertheless, the numbers speak for themselves .

Of course, the curiosity factor alone would drive any developer to want to understand this, right? For now, I can only give you a hint: MSIL . This hint will be explained later in the section "Same Reference Object, Different Languages." Beyond that, it will really help if you first review the VB .NET example you will find in the next section. In that section, the string concatenation functionality has been reproduced using VB .NET.

VB .NET Example

For your bilingual learning pleasure , Listing 9-7 presents the StringBuilderExampleVB project. After you review the code, please take a moment to actually execute the program. The program will output a few lines to the console window.

Listing 9-7: The StringBuilderExampleVB Project
start example
 Module Module1   Sub Main()       Dim i As Int32       Dim myStartTick As Int32       Dim myFinishTick As Int32           Console.WriteLine("Begin String Builder Example")       Console.WriteLine(String.Empty)           'Start logic for String Compare       myStartTick = System.Environment.TickCount       Dim myString1 As New String("I am creating .NET Garbage to be Collected")       For i = 0 To 9999           myString1 = myString1 & " * One Piece of Garbage to be collected * "       Next           myFinishTick = System.Environment.TickCount       Console.WriteLine("Milliseconds for traditional String concatenation: ")       Console.WriteLine(myFinishTick - myStartTick)           'Start logic for StringBuilder Compare using Default size of 16       myStartTick = System.Environment.TickCount       Dim sb As New System.Text.StringBuilder()       sb.Append("I am creating .NET Garbage to be Collected : ")       For i = 0 To 9999           sb.Append(" * One Piece of Garbage to be collected * ")       Next           myFinishTick = System.Environment.TickCount       Console.WriteLine("Milliseconds for StringBuilder - using default Size: ")       Console.WriteLine(myFinishTick - myStartTick)       'Start logic for StringBuilder(500) Compare       myStartTick = System.Environment.TickCount       Dim sb1 As New System.Text.StringBuilder(500)       sb1.Append("I am creating .NET Garbage to be Collected : ")       For i = 0 To 9999           sb1.Append(" * One Piece of Garbage to be collected * ")       Next       myFinishTick = System.Environment.TickCount       Console.WriteLine("Milliseconds for StringBuilder - initialized Size ")       Console.WriteLine(myFinishTick - myStartTick)       myString1 = Nothing       sb = Nothing       sb1 = Nothing       Console.WriteLine("Press Enter to Exit")       Console.ReadLine()       End Sub End Module 
end example
 

The lines in Listing 9-8 are written to the console window when StringBuilderExampleVB is executed.

Listing 9-8: The Lines Written to the Console Display for the StringBuilderExampleVB Program
start example
 Begin String Builder Example     Milliseconds for traditional String concatenation: 89839 Milliseconds for StringBuilder - using default Size:  41  Milliseconds for StringBuilder - initialized Size  30  Press Enter to Exit 
end example
 

Wow! Now, that is a big difference in performance (as shown in Listing 9-8). When you use VB .NET, it is easy to see why people get so excited about the StringBuilder class.

Tip  

The .NET Framework StringBuilder class has other very useful methods. Although the Append (and ToString) method seems to get all the attention, the StringBuilder class offers the following useful methods: AppendFormat, Insert, Remove, and Replace.

I'd like to point out a few more things about the VB .NET code sample. Let's begin by looking closely at the following code lines copied from Listing 9-7:

 . . . Dim i As Int32 Dim myStartTick As Int32 Dim myFinishTick As Int32 . . . Dim sb As New System.Text.StringBuilder() . . . Dim sb1 As New System.Text.StringBuilder(500) . . . 

Notice that the three Int32 variables are being declared without the word "NEW" in the Dim statement. The Int32 Dim statements are declaring value type objects and relying on their default constructor. On the other hand, both StringBuilder declarations have the word "NEW" included in their Dim statements. The StringBuilder Dim statements are declaring reference type objects and explicitly "calling" the constructor.

As you have certainly noticed, the second StringBuilder in the preceding code (sb1) includes an input parameter of 500. This value of 500 will be input into the constructor. The other StringBuilder in the preceding code (sb) is using the default constructor (in other words, the parameterless constructor). Figure 9-1 shows the list of overloaded constructors for the StringBuilder class as shown in the VS .NET Object Browser.


Figure 9-1: The overloaded constructors of the StringBuilder class

If you explore a bit, it will become apparent that the StringBuilder class (being a reference type object) has inherited from the System.Object class. Further exploration will show that Int32 is derived from the System.ValueType class. That just leaves one additional point to mention: the String class.

Tip  

The String class offers many valuable methods. It would be wise to spend some time familiarizing yourself with them. In some cases, you can replace several lines of programming code with the proper usage of an existing String method (e.g., Format , Trim , TrimEnd , ToLower , and PadLeft , among others). You might compare the String class's collection of methods to the functionality available with the legacy COBOL STRING function. However, you will find that the features of the .NET String class meet and then surpass those of the legacy COBOL STRING function.

The sample application demonstrated a performance concern when using the String class (a reference type object that inherits from System.Object). Although the String class is extremely useful, you really should think twice if you need to implement logic that results in repetitive modifications of the string (i.e., typical concatenation routines). It turns out that the String class is immutable ” in other words, it cannot be changed.

Therefore, the system ends up creating a new String object each time you attempt to update an existing object. This is the reason for the large performance hit. The StringBuilder, on the other hand, allows "underlying" String objects to be updated (without needing to create new String objects).

Same Reference Object, Different Languages

You have learned up to this point that you can take any logic example and easily implement the functionality in either COBOL .NET or VB .NET. However, as you saw in the previous set of sample applications, the performance behavior can vary. The same reference object (in this case, the StringBuilder class) exhibits different behavior when one sample project is compared with the other.

Note  

In the previous sample applications, the fastest COBOL .NET string concatenation processing time (about 40 milliseconds) is nearly equivalent to the second- fastest processing time of the VB .NET application (about 41 milliseconds). Is this just a coincidence ? I think not. My guess is that Fujitsu may have taken the same "optimal" approach in the NetCOBOL for .NET compiler (assuring that the COBOL STRING function was updating the String reference type object in place ) as Microsoft did in the VB .NET compiler (providing the StringBuilder class for in-place updating of the String reference type object).

In the StringBuilderExampleCobol COBOL .NET example, the .NET StringBuilder class performed less efficiently than the intrinsic COBOL STRING function. On the other hand, in the StringBuilderExampleVBVB .NET example, the StringBuilder class performed more efficiently than the intrinsic Visual Basic String function. Here is why.

I mentioned earlier (when reviewing the StringBuilderExampleCobol results) that MSIL was a hint. As you know, MSIL is an abbreviation for Microsoft intermediate language. And it is in reviewing the resulting MSIL for both StringBuilderExampleCobol and StringBuilderExampleVB that the explanation lies.

Using the ILDASM tool (at the command-prompt level) to review the MSIL for each sample project executable, you will be able to locate MSIL code showing the use of the StringBuilder class. Please review the MSIL snippets from each sample project in Listings 9-9 and 9-10. First, Listing 9-9 shows the MSIL for the COBOL .NET project. Notice the five original lines of code that directly relate to the StringBuilder class (these are the lines that are prefixed with a double slash [//] ”for example, //000054 through //000058).

Listing 9-9: A Portion of the MSIL for the COBOL .NET Sample Application
start example
 //000054: 000540 INVOKE SYS-STRINGBUILDER "NEW"       IL_0453: newobj instance void class  [mscorlib]System.Text.StringBuilder::.ctor()       IL_0458: stloc       __Temp001F06D0       IL_045c: ldloc       __Temp001F06D0       IL_0460: stsfld class  [mscorlib]System.Text.StringBuilder MAIN::SB_001CEB18 //000055: 000550     RETURNING sb. //000056: 000560     INVOKE sb "Append"       IL_0465: ldsfld    class  [Fujitsu.COBOL]Fujitsu.COBOL.Runtime.ProgramControl.ILI MAIN ::__ILI       IL_046a: ldloca    __LocalTempCOBOLDATA0       IL_046e: initobj   valuetype [Fujitsu.COBOL]Fujitsu.COBOL.COBOLData       IL_0474: ldloca    __LocalTempCOBOLDATA0       IL_0478: ldsfld    unsigned int8[] MAIN::__LiteralArea       IL_047d: ldc.i4    0x10a       IL_0482: ldc.i4.s  45       IL_0484: call      instance void  [Fujitsu.COBOL]Fujitsu.COBOL.COBOLData ::.ctor(unsigned int8[],int32,int32)       IL_0489: ldloc     __LocalTempCOBOLDATA0      IL_048d: ldc.i4.s 45      IL_048f: ldloca    __LocalTempCOBOLDATA0      IL_0493: initobj   valuetype [Fujitsu.COBOL]Fujitsu.COBOL.COBOLData      IL_0499: ldloca    __LocalTempCOBOLDATA0      IL_049d: ldloc     __Temp001F06EC      IL_04a1: ldc.i4.0      IL_04a2: ldc.i4.s  90      IL_04a4: call      instance void  [Fujitsu.COBOL]Fujitsu.COBOL.COBOLData ::.ctor(unsigned int8[],int32,int32)      IL_04a9: ldloc    __LocalTempCOBOLDATA0      IL_04ad: ldc.i4.s 90      IL_04af: ldloca   __Temp001F0698      IL_04b3: call     void  [Fujitsu.COBOL]Fujitsu.COBOL.Runtime.Intrinsics.Unicode ::JMP8UCS2(class  [Fujitsu.COBOL]Fujitsu.COBOL.Runtime.ProgramControl.ILI, valuetype [Fujitsu.COBOL]Fujitsu.COBOL.COBOLData,int32, valuetype [Fujitsu.COBOL]Fujitsu.COBOL.COBOLData,int32,int32&)      IL_04b8: ldloc   __Temp001F0698      IL_04bc: brfalse.s IL_04d2      IL_04be: ldloc   __Temp001F06EC      IL_04c2: ldc.i4.0      IL_04c3: ldloc    __Temp001F0698      IL_04c7: call     string  [Fujitsu.COBOL]Fujitsu.COBOL.Runtime.OmeLib.Setc ::ConvCobolTOString(unsigned int8[],int32,int32)      IL_04cc: stloc    __Temp001F0708      IL_04d0: br.s     IL_04d7      IL_04d2: ldnull      IL_04d3: stloc     __Temp001F0708      IL_04d7: ldsfld    class  [mscorlib]System.Text.StringBuilder MAIN::SB_001CEB18      IL_04dc: ldloc   __Temp001F0708      IL_04e0: call    instance class  [mscorlib]System.Text.StringBuilder class  [mscorlib]System.Text.StringBuilder ::Append(string)      IL_04e5: stloc    __Temp001F0724      IL_04e9: ldloc    __Temp001F0724      IL_04ed: stsfld class  [mscorlib]System.Text.StringBuilder MAIN::SB_001CEB18 //000057: 000570    USING BY VALUE "I am creating .NET Garbage to be Collected : " //000058: 000580    RETURNING sb. 
end example
 

Listing 9-10 shows a snippet of MSIL for the VB .NET project. In this case, there are two lines of original code related to the StringBuilder class (again, these are the two lines prefixed with a double slash [//] ”for example, //000025 and //000026).

Listing 9-10: A Portion of the MSIL for the VB .NET Sample Application
start example
 //000025:            Dim sb As New System.Text.StringBuilder()  IL_0068:  newobj    instance void [mscorlib]System.Text.StringBuilder::.ctor()  IL_006d:  stloc.s   sb //000026:      sb.Append("I am creating .NET Garbage to be Collected : ")  IL_006f:  ldloc.s    sb  IL_0071:  ldstr      "I am creating .NET Garbage to be Collected : "  IL_0076:  callvirt   instance class [mscorlib]System.Text.StringBuilder  [mscorlib]System.Text.StringBuilder::Append(string)  IL_007b: pop 
end example
 

Ignoring the difference in the number of original code lines, please note the difference in the resulting MSIL lines. It is, after all, the MSIL that counts. [3] Because both examples end up accessing the System.Text.StringBuilder class via mscorlib.dll (shown in the MSIL listings as [mscorlib]), it is fair to say that you are comparing "apples to apples." The conclusion (apparently) is that one of the "apples" has a significant amount of MSIL overhead added. This explains the inconsistent performance behavior of the StringBuilder class.

Tip  

In the future, when you need to do repetitive string concatenation, I suggest that you use the intrinsic STRING function in your COBOL .NET programs. On the other hand, VB .NET programs should use the .NET StringBuilder class (a reference type object). This suggestion would stand until Fujitsu modifies their compiler to produce more streamlined MSIL code (for implementing the StringBuilder class).

This behavior observation certainly underscores my point. When you code in .NET, choose your objects wisely ”both value type objects and reference type objects. Think about the variables involved in your application. Experiment with the .NET Framework classes to find the most efficient coding combinations. Have fun.

Now you'll drill down deeper into the usage of value types and reference types. The following set of sample applications will help set the stage for further discussion.

More Sample Code

This next set of sample applications (ValueTypeSampleVB and ValueTypeSampleCobol) illustrate a difference between using value types and reference types. These samples focus on the "usage" factor ”showing that how you use value type objects and reference type objects does in fact make a difference.

COBOL .NET Example

This sample application demonstrates the use of the structure value type object and the basic reference type class object. For each object type, you will see an Integer and a String used.

Note  

The group data type is COBOL .NET's way of providing intrinsic support for the .NET structure value type object.

You will also notice that I have broken the ValueTypeSampleCobol COBOL .NET sample application code into three physical files:

  • Program1.cob

  • mybarInt.cob

  • mybarStr.cob

I did this to demonstrate a common approach to organizing your own classes. Please review the following code from the three COBOL .NET ".cob" files. Listing 9-11 presents the ValueTypeSampleCobol Program1.cob file.

Listing 9-11: The ValueTypeSampleCobol Program1.cob File
start example
 000010* 'ValueTypeSampleCobol Console Application 000020 CLASS-ID. A-CLASS.  000030 ENVIRONMENT DIVISION. 000040 CONFIGURATION SECTION. 000050 REPOSITORY. 000060* 000070* ADD References for .NET Framework Classes 000080 000090    CLASS SYS-ENVIRONMENT_Obj AS "System.Environment" 000100    PROPERTY PROP-TickCount AS "TickCount" 000110  000120* ADD references for the Classes created in this Project 000130    CLASS mybarInt 000140    CLASS mybarStr. 000150  000160 STATIC. 000170 PROCEDURE DIVISION. 000180 METHOD-ID. MAIN. 000190 DATA DIVISION. 000200 WORKING-STORAGE SECTION. 000210 01 OBJ OBJECT REFERENCE A-CLASS. 000220 PROCEDURE DIVISION. 000230    INVOKE A-CLASS "NEW" RETURNING OBJ. 000240 END METHOD MAIN. 000250 END STATIC. 000260  000270 OBJECT. 000280 DATA DIVISION. 000290 WORKING-STORAGE SECTION. 000300 PROCEDURE DIVISION. 000310* 000320* The method below is known as the CONSTRUCTOR 000330 METHOD-ID. NEW. 000340 DATA DIVISION. 000350 WORKING-STORAGE SECTION. 000360 PROCEDURE DIVISION. 000370    INVOKE SELF "A-METHOD". 000380 END METHOD NEW. 000390*  000400 METHOD-ID. A-METHOD IS PUBLIC. 000410 DATA DIVISION. 000420 WORKING-STORAGE SECTION. 000430* Misc Fields  000440  77 myStartTick PIC S9(9) COMP-5. 000450  77 myFinishTick PIC S9(9) COMP-5. 000460  77 myComputedTick PIC S9(9) COMP-5. 000470  77 myDisplayTick PIC Z(9). 000480  77 i PIC S9(9) COMP-5. 000490  01 NULL-X PIC X(1). 000500  000510*  NOTE: A Group Data Type is COBOL.NET's intrinsic support 000520*  for .NET Structures 000530 000540* Create my own Structure that contains a Int32 (equivalent) 000550 01 WS-myfooInteger_Test1. 000560    05 WS-myInt PIC S9(9) USAGE COMP-5. 000570 01 WS-myfooInteger_Test2. 000580    05 WS-myInt PIC S9(9) USAGE COMP-5. 000590 000600* Create my own Structure that contains a String 000610 01 WS-myfooString_Test1. 000620    05 WS-myString PIC X(30). 000630 01 WS-myfooString_Test2. 000640    05 WS-myString PIC X(30). 000650  000660 01 mybarInt_Obj_Test1 OBJECT REFERENCE mybarInt. 000670 01 mybarInt_Obj_Test2 OBJECT REFERENCE mybarInt. 000680 01 mybarStr_Obj_Test1 OBJECT REFERENCE mybarStr. 000690 01 mybarStr_Obj_Test2 OBJECT REFERENCE mybarStr. 000700  000710 LINKAGE SECTION. 000720 PROCEDURE DIVISION. 000730*  000740    DISPLAY "Starting the ValueTypeSampleCobol Console Application." 000750    Display " " 000760    SET myStartTick to PROP-TickCount of SYS-ENVIRONMENT_Obj. 000770    PERFORM VARYING i  000780      FROM 0 BY 1 UNTIL i >= 999999 000790         MOVE 1 to WS-myfooInteger_Test1 000800         MOVE 2 to WS-myfooInteger_Test2 000810         MOVE WS-myfooInteger_Test1 to WS-myfooInteger_Test2 000820    END-PERFORM 000830    SET myFinishTick to PROP-TickCount of SYS-ENVIRONMENT_Obj. 000840  000850  000860    COMPUTE myComputedTick = (myFinishTick - myStartTick) 000870    MOVE myComputedTick to myDisplayTick 000880    DISPLAY "Total milliseconds: Integer Structures: " myDisplayTick  000890             Display " " 000900 000910    SET myStartTick to PROP-TickCount of SYS-ENVIRONMENT_Obj. 000920    PERFORM VARYING i  000930       FROM 0 BY 1 UNTIL i >= 999999 000940          MOVE "This is the String" to WS-myfooString_Test1 000950          MOVE "This is the Second String" to WS-myfooString_Test2 000960          MOVE WS-myfooString_Test1 to WS-myfooString_Test2 000970    END-PERFORM 000980    SET myFinishTick to PROP-TickCount of SYS-ENVIRONMENT_Obj. 000990  001000    COMPUTE myComputedTick = (myFinishTick - myStartTick) 001010    MOVE myComputedTick to myDisplayTick 001020    DISPLAY "Total milliseconds: String Structures: " myDisplayTick  001030    Display " " 001040 001050    SET myStartTick to PROP-TickCount of SYS-ENVIRONMENT_Obj. 001060    PERFORM VARYING i  001070       FROM 0 BY 1 UNTIL i >= 999999 001080          INVOKE mybarInt "NEW" USING BY VALUE 1 001090                   RETURNING mybarInt_Obj_Test1 001100          INVOKE mybarInt "NEW" USING BY VALUE 2 001110                   RETURNING mybarInt_Obj_Test2 001120          SET mybarInt_Obj_Test2 TO mybarInt_Obj_Test1 001130    END-PERFORM 001140    SET myFinishTick to PROP-TickCount of SYS-ENVIRONMENT_Obj. 001150  001160    COMPUTE myComputedTick = (myFinishTick - myStartTick) 001170    MOVE myComputedTick to myDisplayTick 001180    DISPLAY "Total milliseconds: Reference -Int Objects: " myDisplayTick  001190    Display " " 001200 001210    SET myStartTick to PROP-TickCount of SYS-ENVIRONMENT_Obj. 001220    PERFORM VARYING i  001230       FROM 0 BY 1 UNTIL i >= 999999 001240          INVOKE mybarStr "NEW" USING BY VALUE "This is the String" 001250                   RETURNING mybarStr_Obj_Test1 001260          INVOKE mybarStr "NEW" USING BY VALUE "This is the Second String" 001270                   RETURNING mybarStr_Obj_Test2 001280          SET mybarStr_Obj_Test2 TO mybarStr_Obj_Test1 001290    END-PERFORM 001300    SET myFinishTick to PROP-TickCount of SYS-ENVIRONMENT_Obj. 001310  001320    COMPUTE myComputedTick = (myFinishTick - myStartTick) 001330    MOVE myComputedTick to myDisplayTick 001340    DISPLAY "Total milliseconds: Reference - Str Objects: " myDisplayTick 001350    Display " " 001360  001370    Display "The Test is now complete.". 001380    DISPLAY "Enter X and Press Enter to Exit.". 001390    ACCEPT NULL-X. 001400  001410 END METHOD A-METHOD. 001420* 001430 END OBJECT. 001440 END CLASS A-CLASS. 
end example
 

The preceding Static class instantiates objects using the two classes shown in Listings 9-12 and 9-13.

Listing 9-12: The ValueTypeSampleCobol mybarInt.cob File
start example
 000010 IDENTIFICATION DIVISION. 000020 CLASS-ID. mybarInt. 000030 ENVIRONMENT DIVISION. 000040 CONFIGURATION SECTION. 000050 REPOSITORY. 000060    CLASS SYS-INT32_Obj AS "System.Int32". 000070 OBJECT. 000080 PROCEDURE DIVISION. 000090 METHOD-ID. NEW. 000100 DATA DIVISION. 000110 WORKING-STORAGE SECTION. 000120 01 myInt1_Obj OBJECT REFERENCE SYS-INT32_Obj. 000130 LINKAGE SECTION. 000140 01 myInt1 PIC S9(9) USAGE COMP-5. 000150 PROCEDURE DIVISION USING BY VALUE myInt1. 000160    SET myInt1_Obj TO myInt1. 000170 END METHOD NEW. 000180 END OBJECT. 000190 END CLASS mybarInt. 
end example
 
Listing 9-13: The ValueTypeSampleCobol mybarStr.cob File
start example
 000010 IDENTIFICATION DIVISION. 000020 CLASS-ID. mybarStr. 000030 ENVIRONMENT DIVISION. 000040 CONFIGURATION SECTION. 000050 REPOSITORY. 000060    CLASS SYS-STRING_Obj AS "System.String". 000070 OBJECT. 000080 PROCEDURE DIVISION. 000090 METHOD-ID. NEW. 000100 DATA DIVISION. 000110 WORKING-STORAGE SECTION. 000120 01 myStr_Obj OBJECT REFERENCE SYS-STRING_Obj. 000130 LINKAGE SECTION. 000140 01 myStr OBJECT REFERENCE SYS-STRING_Obj. 000150 PROCEDURE DIVISION USING BY VALUE myStr. 000160    SET myStr_Obj TO myStr. 000170 END METHOD NEW. 000180 END OBJECT. 000190 END CLASS mybarStr. 
end example
 

After you run the ValueTypeSampleCobol sample application, the lines in Listing 9-14 are written to the console display window.

Listing 9-14: Console Display Window Output from ValueTypeSampleCobol
start example
 Starting the ValueTypeSampleCobol Console Application. Total milliseconds for Integer Structures:          40 Total milliseconds for String Structures:         4647 Total milliseconds for Reference -Int Objects:    7561 Total milliseconds for Reference - Str Objects:  21831 The Test is now complete. Enter X and Press Enter to Exit. 
end example
 

The console display output is quite revealing . Please take a moment to notice the significant difference (in processing time) between the reference type String and value type Integer usage. A significant difference is also apparent when you compare the value type structure usage to the reference type object usage.

Would you have guessed that such a big performance difference would surface? It certainly is good to be aware of the "cost" of one coding design over another. Things that appear to be subtle choices (e.g., object types, data types, and so forth) can make a big difference in the performance of your application.

In the next section, you'll take a look at the same value/reference object type comparison, this time using VB .NET.

VB .NET Example

Please take a moment to review the sample application in Listing 9-15. You will see the definitions for two structures (myfooInteger and myfooString) and two classes (mybarInt and mybarStr). Near the end of the application, you will notice that there is one larger class (MyFirstClass) defined that actually uses the other defined structures and classes. As with the other applications, this ValueTypeSampleVB application is a console application. If you can, please execute the application to generate the console display output.

Listing 9-15: The ValueTypeSampleVB Application
start example
 'ValueTypeSampleVB Console Application 
 'Create my own Public Structure Public Structure myfooInteger        Public myInt As System.Int32            'Define a Constructor Method        Public Sub New(ByVal Input As Int32)               Me.myInt = Input        End Sub End Structure     Public Structure myfooString        Public myString As System.String            'Define a Constructor Method        Public Sub New(ByVal Input As String)               Me.myString = Input        End Sub End Structure     'Create my own Class Public Class mybarInt        Public myInt1 As System.Int32            'Define a Constructor Method        Public Sub New(ByVal Input As Int32)               Me.myInt1 = Input        End Sub End Class     'Create my own Class Public Class mybarStr        Public myStr As System.String            'Define a Constructor Method        Public Sub New(ByVal Input As String)               Me.myStr = Input        End Sub End Class     Class MyFirstClass       Shared Sub Main()              Console.WriteLine _              ("Starting the ValueTypeSampleVB Console Application.")              Dim tick1 As Integer = System.Environment.TickCount              Dim i As Integer              For i = 0 To 999999                    Dim test1 As myfooInteger = New myfooInteger(1)                    Dim test2 As myfooInteger = New myfooInteger(2)                    test2 = test1              Next              Dim tick2 As Integer = System.Environment.TickCount              Console.WriteLine _       ("Total milliseconds for Integer Structures: " & _       (tick2 - tick1))             Console.WriteLine(" ")                 Dim tick1a As Integer = System.Environment.TickCount             Dim ia As Integer             For ia = 0 To 999999                    Dim test1 As myfooString = _                    New myfooString("This is the String")                    Dim test2 As myfooString = _                    New myfooString("This is the Second String")                    test2 = test1             Next             Dim tick2a As Integer = System.Environment.TickCount             Console.WriteLine _       ("Total milliseconds for String Structures: " & _       (tick2a - tick1a))             Console.WriteLine(" ")                 Dim tick1d As Integer = System.Environment.TickCount             Dim id As Integer             For id = 0 To 999999                   Dim test1 As mybarInt = New mybarInt(1)                   Dim test2 As mybarInt = New mybarInt(2)                   test2 = test1             Next             Dim tick2d As Integer = System.Environment.TickCount             Console.WriteLine _       ("Total milliseconds for Reference - Integer Type Objects Loop: " & _       (tick2d - tick1d))              Console.WriteLine(" ")                   Dim tick1e As Integer = System.Environment.TickCount                   Dim ie As Integer                   For ie = 0 To 999999                        Dim test1 As mybarStr = _                        New mybarStr("This is the String")                        Dim test2 As mybarStr = _                        New mybarStr("This is the Second String")                        test2 = test1                   Next                   Dim tick2e As Integer = System.Environment.TickCount                   Console.WriteLine _       ("Total milliseconds for Reference - String Type Objects Loop: " & _       (tick2e - tick1e))             Console.WriteLine(" ")                 Console.WriteLine("The Test is now complete. Press Enter to Exit")             Console.ReadLine()       End Sub End Class 
end example
 

Please review the output shown in Listing 9-16 that is displayed in the console window when the ValueTypeSampleVB application is executed.

Listing 9-16: Console Output Display for ValueTypeSampleVB
start example
 Starting the ValueTypeSampleVB Console Application. Total milliseconds for Integer Structures: 50 Total milliseconds for String Structures: 60 Total milliseconds for Reference - Integer Type Objects Loop: 320 Total milliseconds for Reference - String Type Objects Loop: 341 The Test is now complete. Press Enter to Exit 
end example
 

Now, aren't those displayed numbers revealing? Notice the pattern. The value type objects (the structures) that are defined to contain just an Integer will generally be faster to process than the other structures (in this case, those defined to contain a String). Additionally, the same pattern exists with the performance of the reference type objects. The Integer type reference object is generally faster than the String reference type object.

Note  

There are times when you will choose a particular data type because you absolutely need that particular data type. In those cases, these performance issues will have little meaning. However, when you actually have a choice, then of course you will want to choose the most efficient data type for your application.

For the structures, it boils down to this fact: When you define your value type data type to be part of a structure, it will actually have its value stored with the structure on the stack. These "true" value type structures will generally process faster. On the other hand, when the reference type object was defined as part of a structure, you were actually just storing the reference pointer there. Naturally, the pointer (just like any other reference type) is pointing to allocated space on the heap. This difference can help explain the relative variance in the processing times. [4]

Tip  

Always strive to allocate your value type and reference type objects using specific data types. In other words, good coding practices might result in rare usage of the System.Object data type and more common usage of System.Int32, System.String, and so on. Following this guideline, your code will typically run faster [5] and be more maintainable .

Continuing with the sample code output observation, you will notice that the reference type objects (created in the form of our own classes) produced similar processing variances (relatively speaking). Additionally, you will notice that the processing times are higher for the reference types than for the value type objects. Given the extra heap storage allocation concerns, this variance is to be expected. There are many times that you will appropriately choose to use reference type objects in spite of this apparent overhead. The fact remains that reference type objects have a lot to offer, but there is a price to pay. Investigate, experiment, and choose wisely.

In this section, I contrasted value type objects against reference type objects. Additionally, I reviewed general object usage concerns and potential performance issues. In the remaining section of the chapter, you'll explore yet another object usage concern: visibility and availability.

[3] This is partially true. The Microsoft intermediate language is ultimately compiled into machine code by the .NET just-in-time (JIT) compiler.

[4] As the byte size of your structure increases , the processing time comparison "gap" will tend to narrow.

[5] This did not appear to be the fact with some of my COBOL .NET testing. With COBOL .NET, the System.Object usage actually yielded faster results than the more specific data types of Integer and String. Interesting, to say the least!




COBOL and Visual Basic on .NET
COBOL and Visual Basic on .NET: A Guide for the Reformed Mainframe Programmer
ISBN: 1590590481
EAN: 2147483647
Year: 2003
Pages: 204

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net