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.)
(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
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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
The lines in Listing 9-8 are written to the console window when StringBuilderExampleVB is executed.
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
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.
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).
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).
//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.
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).
//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
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.
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.
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.
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.
The preceding Static class instantiates objects using the two classes shown in Listings 9-12 and 9-13.
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.
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.
After you run the ValueTypeSampleCobol sample application, the lines in Listing 9-14 are written to the console display window.
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.
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.
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.
'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
Please review the output shown in Listing 9-16 that is displayed in the console window when the ValueTypeSampleVB application is executed.
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
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!