by Justin Rogers

Chapter 3: Datatypes, Arrays, and Strings

In This Chapter

  • Strongly Typing in JScript .NET 
  • Basic Datatypes
  • Declaring and Typing Arrays 
  • Using the String Object 

This chapter discusses the declaration of variables in JScript .NET, the correlation of variables with common language runtime (CLR) types, and how the compiler makes intelligent decisions between legacy JScript semantics and new performance-oriented JScript .NET semantics.

The discussion in this chapter moves from the new data typing syntax used in JScript .NET straight to the base language datatypes. A thorough explanation of strongly typing variables is followed an exploration of how to create arrays. Both the legacy JScript style array and the newer CLR-style array are discussed. The only other remaining datatype of importance is the string. Strings are the basis for the majority of programs.

Strongly Typing in JScript .NET

Strongly typed variables allow the JScript .NET compiler to use appropriately sized internal data structures when performing operations. Typed data doesn't have to be converted to and from different datatypes when basic operations and assignments occur. It always occupies the appropriate amount of memory space for its size, thus allowing the developer to optimize for size and use smaller datatypes when appropriate. Furthermore, it allows the compiler to remove various checks that were once required when the same variable could at one point be a string and at another point an integer.

How to Strongly Type Variables

So how do you start taking advantage of these new improvements in JScript .NET? You declare variables to give them scope and you type variables so that the compiler uses the optimized operations and removes the type checks during runtime. You declare all variables by using the var keyword. Any additional access modifiers needed to complete the declaration can precede the var keyword. An additional modifier, a custom attribute, can also be applied (both access modifiers and attributes are discussed in Chapter 6, "Creating Classes"). The following example demonstrates how to declare a nontyped variable named UnknownVar:

// Untyped variable declaration in Global Code
var UnknownVar;

Note

For a complete explanation of the access modifiers and custom attributes, you can jump to Chapter 6, which discusses class members. The following are some of the important modifiers you can learn more about in Chapter 6:

  • The public keyword makes variables available outside the current scope but does not make the variable global.
  • The static keyword makes variables persistent after they go out of scope, effectively making the variable a global variable yet limited to being global inside the current scope, without additional modifiers.
  • The private keyword declares variables as locally scoped variables that are available only to code within the current scope.

The scope-related modifiers are available only from within a class definition. So, for the most part, you won't be using them in the early chapters of this book.


Simply declaring a variable doesn't do anything more than give that variable a scope. If you performed a declaration within a class, the variable would be scoped to that class. If you performed the same declaration within a method, the variable would be scoped to the function level. Because the declaration in the preceding example occurs in global code, it creates a global variable named UnknownVar of type Object. This doesn't provide any performance benefits, and the variable isn't strongly typed. To strongly type a variable, you use the syntax name:type (where name is the name of the variable and type is the variable type). The colon after name tells the compiler that you want to strongly type the variable. The following is an example of strongly typing several variables (notice that whitespace doesn't matter to the compiler):


Note

Notice that the introduction of whitespace (that is, space characters, tabs, and carriage returns) in the code doesn't matter to the compiler and doesn't affect the way the compiler parses the source code.


var StringVar:String
var IntVar  :Int32
var BoolVar:  Boolean
var ArrayVar  :  String[]

All these statements create valid variables that are strongly typed. The whitespace between the variable name, colon, and type name doesn't matter because the compiler ignores whitespace. You can make the type name a short name, or you can make it a fully qualified name by appending the namespace to the type name. After you type this code, the compiler will throw warnings if you try to assign invalid values. Assigning to StringVar an object of some type that can't be converted to a string would result in a compiler error, as would assigning a string to IntVar.

Providing Initial Values for Variables

You can initialize variables with default values. You might want to do this to ensure that you have some working values before the code starts operating on them. You can also see some of the compiler errors by creating some initialization expressions where the type of the initializing value is of a different type than the variable to which the value is being assigned. For example, the following example assigns the string constant "Hello" to StringVar and the value 10 to IntVar:

import System;
import System.Collections;

// Valid Initializers
var StringVar:String = "Hello";
var IntVar:Int32 = 10;
var HashVar:Hashtable = new Hashtable();

// tricky Initializers
var String2Var:String = 23;
var Int2Var:Int32 = "27";

// Invalid initializers
var IntVar:Int32 = "Hello";

The new keyword creates a new instance of a hashtable. The next assignment is rather tricky—it assigns a numeric value to String2Var. You would think that this would result in a compiler error, but JScript converts the numeric constant 23 to the string constant "23". Furthermore, the JScript compiler turns the string constant "27" into the numeric constant 27 so that Int2Var contains a numeric value. The next couple initializers throw some errors because you can't assign a string constant that can't be parsed as a number into a numeric variable. Notice that the example does not provide invalid syntax for assigning initializer values to a String object because any type or value can be converted to a string to be assigned to the String object. We'll discuss this further in the section "Using the String Object," later in this chapter.


Tip

Every object in the .NET framework supports the ToString() method, which returns the string representation of an object (which can be a name, a serialized version of the object, or any other textual representation). Therefore, assigning any value or object to a String object does not throw an error in JScript .NET because JScript converts any objects or initializer values into string values before continuing. Note that this can cause strange values to be assigned to string variables and can sometimes cause code to go down a code path you wouldn't normally expect.


Performance and Strongly Typed Variables

The performance benefits of strongly typing variables are extremely obvious when you look at the process of performing operations on each type of code. You can directly operate on strongly typed variables without the extra level of indirection that is required for traditional JScript variables. In JScript, the underlying object currently referenced by a variable is checked for type and then converted, if necessary, to a type that is compatible with the current operation. In JScript .NET code, the operation occurs automatically. If the type is incompatible, an exception is thrown, and this forces you, as the programmer, to preemptively check variable operations or to wrap them in try...catch blocks (discussed in Chapter 8, "Exception Handling") and handle the exceptions. It also means that in well-programmed code, several operations are saved for each variable operation.

A second performance advantage has to do with the JScript compiler and is a direct result of not typing variables. The JScript compiler team assumed that there would be quite a bit of legacy code to deal with, and it wanted to see performance benefits without having to recode all the existing samples and programs. The team decided that if the context of a variable and all assignments to that variable could be determined within a local scope, it could guess the type of a variable. Basically, this means that performance-oriented variables, such as loop variables and counters, are strongly typed if they are declared in a local scope. To declare a variable in local scope, you use the var keyword on the variable, but it doesn't have to be strongly typed because the compiler infers the type. This option is not available for global variables or variables that aren't declared within a particular scope (because the compiler sets them as global variables).

The Flexibility of Native JScript Variables

All the existing flexibility of native JScript variables is still available in JScript .NET. All String variables, for instance, are stored as JScript strings and are converted to CLR strings as needed at runtime. This conversion is completed by some helper functions in the JScript namespace that are called whenever a complex conversion needs to be made. Any variable declared as an Array object is considered to be a JScript array, and any variable declared as a System.Array object is considered to be a CLR array. The automatic conversions are in place, and interaction with the .NET platform is not an issue because the compiler provides services for conversion of all native JScript types to their equivalent CLR types. So, if flexibility and backward compatibility are issues when you're working with variables, or there is some behavior about classic JScript that you loved to use, then you should feel free use it. Just be aware of the performance issues involved and of the new syntaxes that you can take advantage of for creating faster and smaller code.

Basic Datatypes

Both JScript .NET and the CLR provide many basic datatypes for nearly any operation. This section examines the primitive types available in JScript .NET, as well as their CLR counterparts. This section also discusses some of the complex datatypes, String and Array, and a few of the CLR types that aren't available in the JScript language but are available through the platform.

Primitive Types

Historically, the primitive datatypes of a language have been defined as datatypes that are directly supported by the language through built-in keywords. In addition to having direct language support, primitive types are generally very compact or well defined. (Complex objects and datatypes that take substantial amounts of initialization code don't fit into the category of primitive datatypes.)

The primitive datatypes available in JScript .NET include the numeric datatypes that support integers and floating-pointer numbers, the Boolean datatype that represents a true/false condition, and several special types, null and undefined, that can be used to test whether variables have been initialized.

Integer Numeric Types

JScript has several built-in keywords for describing various integer number types. Each of these types in turn relates to a type in the CLR. All the methods of the JScript types come from the functionality provided by the CLR. JScript supports the byte, short, int, and long keywords. Of these types, the byte type is unsigned, and the short, int, and long types are all signed. JScript doesn't have any additional keywords to specify the sign of a variable. table 3.1 lists each of the JScript types, along with the corresponding CLR type. You'll notice some CLR types without JScript mappings that might come in handy when writing programs.

table 3.1 JScript-to-CLR Type Mapping

JScript .NET Type
CLR Type
Signed
byte
System.Byte
No
 
System.SByte
Yes
short
System.Int16
Yes
 
System.UInt16
No
int
System.Int32
Yes
 
System.UInt32
No
long
System.Int64
Yes
 
System.UInt64
No


You can use the JScript .NET type names and the CLR-type names interchangeably. The CLR has many additional types that aren't available in JScript. Both the UInt32 and UInt64 can come in extremely handy for large number calculations or long-running loops.

Listing 3.1 is a small program that prints out the lower and upper bounds of each of the CLR types. These types have static member variables for their bounds, so a simple call to MinValue and MaxValue can get you exactly what you need.

Listing 3.1 CLR Integer Type Bounds

import System;

// We have to use the actual type names because we are trying to
// make use of the static field members.
Console.WriteLine("{0,-15}{1,-25}{2,-25}", "Type Name",
  "MinValue", "MaxValue");
Console.WriteLine("{0,-15}{1,-25}{2,-25}", "Byte", 
  Byte.MinValue, Byte.MaxValue);
Console.WriteLine("{0,-15}{1,-25}{2,-25}", "SByte", 
  SByte.MinValue, SByte.MaxValue);
Console.WriteLine("{0,-15}{1,-25}{2,-25}", "Int16", 
  Int16.MinValue, Int16.MaxValue);
Console.WriteLine("{0,-15}{1,-25}{2,-25}", "UInt16", 
  UInt16.MinValue, UInt16.MaxValue);
Console.WriteLine("{0,-15}{1,-25}{2,-25}", "Int32", 
  Int32.MinValue, Int32.MaxValue);
Console.WriteLine("{0,-15}{1,-25}{2,-25}", "UInt32", 
  UInt32.MinValue, UInt32.MaxValue);
Console.WriteLine("{0,-15}{1,-25}{2,-25}", "Int64", 
  Int64.MinValue, Int64.MaxValue);
Console.WriteLine("{0,-15}{1,-25}{2,-25}", "UInt64", 

  UInt64.MinValue, UInt64.MaxValue);

Note

Listing 3.1 takes advantage of some pretty neat features of the Console object. So far, the only code you have seen that uses Console object output has been extremely simple. Listing 3.1 uses a format string that is similar to that used with the C function printf. Each of the items is then displayed as a parameter. Although we pass three parameters in Listing 3.1, the function can normally handle up to four, and beyond four requires that you pass in a special array of parameters rather than pass each parameter individually.

The format string consists of a parameter format specifier in between each pair of braces. Each format specifier has the offset to the parameter it should use, and then some additional formatting rules are applied (for example, each item take up a certain amount of spaces [either 15 or 25] and each item should be left aligned [by using the minus symbol]). Other formatting rules are available, and you'll see them in notes throughout the book. For more information, examine the Software Development Kit (SDK) documentation because these formatting rules tend to be different in the various types and objects.


Float Numeric Types

For floating-point numbers, JScript supports both the float and the double keywords. These types map to the CLR types Single and Double, respectively. The Single type represents a 32-bit floating-point number, and the Double type represents a 64-bit floating-point number. You can assign values to floating-point numbers by using the decimal representation, or you can use scientific notation. The syntax for scientific notation in JScript .NET is numberEmantissa, where mantissa is the power of 10 to multiply by the number number. Listing 3.2 shows the upper and lower bounds of both the float and double datatypes, using the static fields.

Listing 3.2 CLR Float Type Bounds

import System;

// JScript assignments
var _float:float = 7.333E4;
var _double:double = 1.2E10;

// We have to use the actual type names because we are trying to
// make use of the static field members.
Console.WriteLine("{0,-15}{1,-15}{2,-15}", "Type Name", 
  "MinValue", "MaxValue");
Console.WriteLine("{0,-15}{1,-15}{2,-15}", "Single", 
  Single.MinValue, Single.MaxValue);
Console.WriteLine("{0,-15}{1,-15}{2,-15}", "Double", 

 Double.MinValue, Double.MaxValue);

The Boolean Type

JScript represents a Boolean typed variable with the keyword Boolean. This object directly converts to the CLR Type System.Boolean and takes up 2 bytes worth of space (the same as an Int16). Along with supporting a variable datatype, JScript .NET can also coerce nearly any expression to return a Boolean result. This allows for very compact code when you're trying to compare types against what their basic values should be.

For numeric datatypes, both floats and integers, any 0 value returns false. Any nonzero value returns true. This means that when checking against 0 in a conditional statement, you can leave out the comparison operator altogether, as in the following example:

var _int:int = 0;
function DoLoops(_int:int) {
  // Check to make sure we are doing at least one loop.
  // If not. Set the value to one.
  if ( !_int ) { _int = 1; }
  // do something
}

You can also check objects to see whether they have been initialized, by using the Boolean type. Any variable that is still null and has not been initialized returns false. Any variable that contains an object reference returns true. The following is the best way to check any objects before using them:

var _obj:Object;

if ( _obj ) {
  // Do something with the object
} else {
  // We have a bad object. Decide what to tell the user.
}

Caution

Be sure you test the preceding example with each of the types that you expect to be used. Depending on the object type, JScript has specific ways of deciding whether to return true or false in the conditional statement. The generic method is to test the object reference against null, but some of the primitive datatypes aren't objects and behave differently, depending on the default value for the datatype. So, keep in mind that testing for null is valid only on objects, and not on primitive datatypes.


Special Types: null and undefined

null and undefined are actually not special types as much as they are special values of types. null and undefined behave identically to one another because the JScript compiler handles the appropriate conversion between null and undefined as necessary. For the most part, you use both null and undefined to determine whether a variable contains a null pointer or a pointer to an object. For this reason, you can't use this method to compare objects that extend System.ValueType, such as the integer types and the Boolean type. Listing 3.3 demonstrates the use of null and undefined.

Listing 3.3 Using null and undefined

import System;

var MyInt:Int32;
var MyString:String;
var MyBoolean:Boolean;
var MyObject = undefined;

if ( MyInt == null ) {
  Console.WriteLine("MyInt == null");
}
if ( MyInt == undefined ) {
  Console.WriteLine("MyInt == undefined");
}

if ( MyString == null ) {
  Console.WriteLine("MyString == null");
}
if ( MyString == undefined ) {
  Console.WriteLine("MyString == undefined");
}

if ( MyBoolean == null ) {
  Console.WriteLine("MyBoolean == null");
}
if ( MyBoolean == undefined ) {
  Console.WriteLine("MyBoolean == undefined");
}

if ( MyObject == null ) {
  Console.WriteLine("MyObject == null");
}
if ( MyObject == undefined ) {
  Console.WriteLine("MyObject == undefined");
}
MyString == null
MyString == undefined
MyObject == null
MyObject == undefined

Notice in Listing 3.3 that even though the Int32 and Boolean variables are not given values, they naturally assume default values. Int32 variables are always 0 and Boolean variables are always false. Because the use of null and undefined is so interchangeable, we will be using null throughout the rest of the book when performing checks on whether variables contain real pointers.

Declaring and Typing Arrays

The use of arrays is integral to almost any programming task. JScript has always had a method for creating arrays that can perform any task, but JScript arrays were never very efficient. The new arrays available through JScript .NET, which can exist side-by-side with JScript arrays, offer both performance and flexibility, but never both at the same time.

Declaring JScript and JScript .NET Arrays

The first step in working with arrays is to declare the array. Let's start by declaring a traditional JScript-style array:

var _JScriptArray:Array = new Array();

_JScriptArray[0] = "123";
_JScriptArray[2000] = 123;
_JScriptArray[500] = new Object();

The type of this array is Array, and the example creates a new instance of the Array class. Note that all the assignments in this example are valid, and that JScript-style arrays are not strongly typed. Everything that goes in the array is of type Object, and everything that comes out is of type Object. Therefore, you can assign a String object to array index 0, int to array index 2000, and new Object to array index 500.

JScript arrays are not bounded. New elements can be assigned on-the-fly. As you make assignments, a length function is updated so that the upper bounds of the array are always known. This comes in handy when you retrieve a JScript array and need to iterate over the contents. If some of the intermediate values were null, then you wouldn't be able to tell the end of the array without the length function. JScript arrays are extremely powerful, but there are some performance tradeoffs involved with them.

The following example demonstrates a variety of methods for finding the true end of a JScript array. The first method searches for null:var _JScriptArray:Array = new Array();
var _int:int = 0;

// Use JScript array like a stack
_JScriptArray[_int++] = "First Element";
_JScriptArray[_int++] = "Second Element";

// Now let's pass this off to a function that doesn't know its length
DumpArray(_JScriptArray);

function DumpArray(_jsArray:Array) {
  var _i:int = 0;
  while(_jsArray[_i]) {
    Console.WriteLine(_jsArray[_i]);
    _i++;
  }
}

This isn't very great code. You need to have a handle on what the top of the array is at all times so that you can add elements on the end. Then you have to search for null. Furthermore, if you put an element at index 10, then the print function would miss it because this function stops at the first null. You could also do better with the length function, and you could take advantage of a couple other functions in the process. For example, the push function allows you to put values on the end of an array and increment the length pointer accordingly, and the pop function returns the very last element of the array, removes it from the end, and decrements the length pointer accordingly. You could rewrite the preceding example as follows:

var _JScriptArray:Array = new Array();

// Use JScript array like a stack
JScriptArray.push("First Element", "Second Element");
JScriptArray[9] = "Tenth Element";

// Now let's pass this off to a function that doesn't know its length
DumpArray(_JScriptArray);

function DumpArray(_jsArray:Array) {
  for ( var _i = 0; i < _jsArray.length(); _i++ ) {
    Console.WriteLine(_jsArray[i]);
  }
}

This code performs the same operations as the preceding example, but it isn't as error prone. After popping the 2 items on the stack, the 3rd through 9th elements are left unassigned, and the 10th value is assigned. The array dynamically sizes to 10 elements as soon as the 10th element is set. The DumpArray function won't stop after the second value and will proceed right through the null elements to element 10 and print the contents.

An important feature of JScript-style arrays is that they are sparse arrays, which means the elements don't have to be contiguous. In the first example you set elements 0, 500, and 2000. Memory isn't allocated for any other elements besides these three, but this introduces a few problems. For instance, the DumpArray function would try to iterate through 2,000 elements, just to print 3 of them. This isn't extremely efficient. CLR-style arrays provide better performance than this, and the next section covers them in detail.

Using CLR-Style Arrays

A CLR-style array has bounds and a definite length, so it can't be expanded and shrunk on-the-fly. This type of array is also strongly typed, meaning you get both compiler and runtime errors for performing incorrect assignments. Every element that comes out of a CLR-style array is of a specific type, so you don't have to check for types before casting. The following example creates a CLR-style array of String objects in JScript .NET. The type is a null array type, and the new keyword is used with a bounded array type to fully initialize the variable:

var _CLRArray:String[] = new String[10];

// We now have a CLR array of Strings with 10 elements
_CLRArray[0] = "Hello";
_CLRArray[9] = "World";
_CLRArray[10] = "Produces an error"; // IndexOutOfRangeException

This array is now strongly typed to String objects. If you attempt to assign any other type of variable (for example, an integer), either the object in question is cast to a String object or you get an InvalidCast error. All objects support the ToString() function, so you will almost never see an InvalidCast error when assigning objects or values to a String array. This can cause very unexpected results. To get an error by assigning an incorrect type, you need to change to another type of array. For example, you could use an array of Int32 objects, such as the following:

var _CLRArray:Int32[] = new Int32[10];

_CLRArray[0] = 1;
// This works because the string can be parsed as a number_CLRArray[1] = "123"; 
_CLRArray[2] = "Hello"; // This fails

With this code, you get a compiler error on the third assignment. It's a basic type mismatch because the compiler knows "Hello" cannot be converted to or parsed as an Int32 object. A number of other array types will also fail when values can't be converted to or from the appropriate type.

You need to be aware that CLR arrays don't have any of the functions you have seen previously in the JScript arrays. You can't push and pop values onto and off arrays, they aren't sparse, and you can't expand them at will. They do have some other properties, though. By using the Length property, you can determine the number of elements in an array. You can use the CopyTo function to transfer array contents from one array to another. You can use the Rank property to determine the number of dimensions an array contains. Take a look at Listing 3.4, which demonstrates the use of the Length property and the CopyTo function.

Listing 3.4 Dynamically Sizing Fixed Arrays

import System;

var _WorkArray:Int32[] = new Int32[10];
var _WorkOffset:Int32 = 0;
var _CopyArray:Int32[];

while(_WorkOffset < 50) {
  while(_WorkOffset < _WorkArray.Length) {
    _WorkArray[_WorkOffset] = _WorkOffset;
    _WorkOffset++;
  }

  if ( _WorkArray.Length < 50 ) {
    _CopyArray = new Int32[_WorkArray.Length + 10];
    _WorkArray.CopyTo(_CopyArray, 0);
    _WorkArray = _CopyArray;
    Console.WriteLine("Expanded Array to " + _WorkArray.Length);
  }
}

_WorkOffset = 0;
while(_WorkOffset < _WorkArray.Length) {
  Console.WriteLine(_WorkArray[_WorkOffset]);
  _WorkOffset++;

}

Listing 3.4 might seem to be quite a bit of code this early in the book. After all, you have not been introduced to while loops, conditional statements, and a number of other features that are used in this listing. So let's walk through the most important parts of Listing 3.4. First, it creates two arrays of type Int32. The work array is given bounds of 10. The copy array is not bounded and remains uninitialized until you fill up the work array. At this point, the copy array is declared to have bounds 10 greater than the work array, the contents of the work array is copied to the copy array, and the copy array is assigned to the work array so that work can continue.

You'll learn more about CLR-style arrays in the sections "Typing Arrays for Performance" and "Multidimensional Array Support" later in this chapter.

Typing Arrays for Performance

JScript-style arrays are slow because they aren't strongly typed. When you get an element in an array that isn't typed, you have no idea what kind of object you're getting. It could be a string, a hashtable, another array, or even a number. You just don't know, and neither does the computer. Therefore, you need code to do type checks and appropriate conversions before the value can be used.

CLR arrays are always typed. In order to create an instance of an array, the underlying type must be given first. Typed arrays can be created in two manners. Earlier in this chapter we talked about the language-specific manner of creating a typed array, which involves using a null array type to which you can later assign bounds by creating a new instance of a typed array. Another method is to create a variable of type System.Array. Later, you can assign it a typed array by using the System.Array.CreateInstance function. This method is extremely powerful because the type of array can be decided at runtime; however, this method also increases the chance of runtime exceptions because array assignments can't be typed and checked during compilation. The following example shows how to use both of these methods of creating typed arrays:

import System;

var _IntegerArray:Int32[] = new Int32[10];
var _StringArray:String[] = new String[10];
var _GenericArray:System.Array;

_GenericArray = _IntegerArray; //Success!
_GenericArray = _StringArray; //Success!

_GenericArray = System.Array.CreateInstance(
  Type.GetType("System.String,mscorlib"),
  10);

Tip

A generic CLR array has to be declared by using type System.Array. JScript interprets Array to mean a JScript array, even if the import System directive is present. This is for legacy purposes and prevents users from typing Microsoft.JScript.Array.


Note several neat things in the preceding example. First, you have two typed arrays that give maximum performance to the application. But you can also assign these arrays to the _GenericArray variable because it is of type System.Array, and all typed arrays inherit from this base class. (You'll learn more about inheritance in Chapters 6 and 7, "Interfaces and Class Members.") _GenericArray can be of any array type, but regardless of type, it has the performance of a typed array, without the compile-time warnings that would exist if you made a poor assignment to an array element. Notice that this example uses a special function on the System.Array class to create a String array. This looks pretty ugly, because it involves several levels of function calls, but it shows that arrays can be created with indeterminate types at runtime. In the example, you have to know the type of the Int32 and the String arrays, but you could have created any type of array by using the CreateInstance function. You can also create arrays that have a variable number of dimensions, as discussed in the following section.

Multidimensional Array Support

JScript .NET supports two types of multidimensional arrays. The first type is a standard equal-bounds array, in which all rows in the array are of the same length. The second type of array is a staggered array, which can have rows of different lengths. Staggered arrays can be effective when you're creating arrays in which some rows might have thousands of elements whereas other rows may contain very few or no elements.

Let's look first at the most common type of array, the equal-bounds array. In the type specifier for this array, you simply add one comma between the brackets for each additional dimension you need to introduce. In the new expression, you provide the bounds for each of the dimensions. This syntax makes creating a multidimensional array very easy. The following code demonstrates the creation of a 10-by-10 array, whose elements you initialize to create a multiplication table:

import System;

var myArray:Int32[,] = new Int32[10,10];
var i:int;
var j:int;

for(i = 0; i < 10; i++){
  for(j = 0; j < 10; j++){
    myArray[i,j] = (i+1)*(j+1);
  }
}

Tip

An overloaded method of Array.CreateInstance allows for the creation of multidimensional arrays as well, and it is often useful when you're creating an array of a type that is not yet known or when the user can specify the number of dimensions (which might be the case with some math applications).


To use this array, you index the array by using an offset for each dimension of the array. You can continue adding offsets for any number of additional dimensions.


Note

Although JScript supports a staggered array, we don't examine the syntax here. It's rather difficult to work with staggered arrays, and there are very few cases in which they are useful and necessary.


Using the String Object

The String object is probably the most widely used and also the most complex object available in JScript .NET. The semantics surrounding this object are fairly complex because there is only one object that is converted on-the-fly between a JScript String object and a CLR String object. All the functions and properties of both objects are available to programmers, and so are all legacy JScript semantics. In other .NET languages, it is very easy to cause exceptions by using a String object. In JScript .NET, you very rarely see this because the compiler does conversions, casting, and initializations for you. This section examines the important aspects of String objects.

Declaring Strings

Two types in JScript (String and System.String) evaluate to the same String object. This is because a JScript String object and a CLR String object both map to a CLR String object internally. Then, special handler functions are called whenever a JScript String object function is called. Therefore, the programmer sees great performance from CLR String objects as well as great flexibility because existing code that works on the String object continues to work. Here's an example of calling both JScript String methods and CLR String methods on the same String object:

var MyString:String = "Hello";

MyString.toString();
MyString.ToString();

This example calls both toString(), which is a JScript function and ToString(), which is a CLR function. For the toString() function, a special call is made into a JScript helper library, and the string is printed according the rules of JScript. The ToString() function, on the other hand, is called directly from the String object and returns according to CLR rules. The two functions behave identically, but they take different code paths. To keep things simple, let's assume there is only one String object and that it simply has two sets of functionality that happen to overlap in certain areas. Where functionality overlaps, we'll be using the CLR versions because they map directly to the underlying object and should be a bit quicker than the JScript versions.

String Manipulation

The String object hosts quite a few methods for manipulating existing strings, creating new strings, and performing quite a few search-and-replace functions for string parsing. At this point, note that any modification to a String object results in a new String object being created because all String objects are immutable and thread safe. You will never run into work on a String object and have it change on you.

All the string manipulation functions can be grouped into several categories:

Each of the following sections provides short descriptions of these functions and a brief example. Some of the examples include output, but in most you should run the code yourself to see the results.

JScript Functions and Properties

Each of the functions and properties discussed in this section exists for compatibility with legacy JScript code. The only nonlegacy property is the length property, which gives the length of a string, in characters. All the old JScript functions that operate on the String object exist in JScript .NET. This can be somewhat strange because in many cases these functions were used when interacting with Hypertext Markup Language (HTML) and return an HTML string result. The following functions are some of the commonly used HTML methods of the String object:

These are just a few of the HTML-related methods, and they have all been part of the JScript standard for quite some time and are well documented both in other books on JScript and on the Web. At this point we will skip the rest of the HTML functions and move on to functions that have CLR equivalents or that can be used for more generic programming than just HTML.

This next set of functions is very helpful in retrieving pieces of the current string:

Listing 3.5 demonstrates each of these functions in use. Note that the Caesar shift functionality (which is a form of simple encryption) of the last part of the listing doesn't work very well, but it handles the encryption (if you can call it that) of Unicode characters outside of the English character set.

Listing 3.5 Character Manipulation in Strings

import System;

var MyString:String = "Getting Character values from Strings";

// Here we are going to get some characters out of the string
for(var i = 0; i < 7; i++) {
  System.Console.Write(MyString.charAt(i));
}
System.Console.WriteLine();

// Now lets print their Unicode values
for(var i = 0; i < 7; i++) {
  System.Console.Write(MyString.charCodeAt(i) + ",");
}
System.Console.WriteLine();

// Here we are going to do a modified Caesar shift
// Modified because it will use computer characters
// and won't wrap the alphabet (a won't wrap to z)
var MyShift:Int32 = 5; // Lets shift by 5 characters
for(var i = 0; i < MyString.length; i++) {
  System.Console.Write(
    String.fromCharCode(MyString.charCodeAt(i) + MyShift)
  );
}

System.Console.WriteLine();

The character code methods in Listing 3.5 are great for working with single characters when character offsets and values are known. To help find character offsets and search strings, you use the indexOf and lastIndexOf methods. These methods find the first or last occurrences of a substring, starting at a given index. The indexOf method takes the substring to search for within the string and an optional beginning index. If it is left null, the beginning index is 0 or the first character in the string. The result is the beginning of the substring within the current string. If the substring is not found, the result is -1. The lastIndexOf method is identical to indexOf method. However, this method searches the string, starting from the end, which means that the second optional parameter, rather than 0, is by default the last character. The return values of indexOf and lastIndexOf are the same.

To retrieve substrings, you use another set of functions. The substr and substring methods provide different ways of getting part of the current string:

Finally, a less well-known method, slice, has the same parameters as substring as well as the same return results for normal operation. The one difference is that the slice method has a different set of rules for treating starting and ending values that are out of bounds for the current string. These rules allow you to index from the end of the string by specifying negative values for either the starting or ending indexes.

Some of these methods might seem strange. Look at Listing 3.6 for an example of their use.

Listing 3.6 Capturing Substrings of a String

var MyString:String = "Parsing strings and obtaining substrings";
var MyStartIndex:Int32;
var MyEndIndex:Int32;

// Success and Failure of indexOf
System.Console.WriteLine("Index of (substring) and (notinstring)");
System.Console.WriteLine(MyString.indexOf("substring") + "," +
  MyString.indexOf("notinstring"));
System.Console.WriteLine();

// indexOf versus lastIndexOf
System.Console.WriteLine("Index of (string) and Last Index of (string)");
System.Console.WriteLine(MyString.indexOf("string") + "," +
  MyString.lastIndexOf("string"));
System.Console.WriteLine();

// substr Semantics
System.Console.WriteLine("The method substr");
System.Console.WriteLine(MyString.substr(8));
System.Console.WriteLine(MyString.substr(8,5));
System.Console.WriteLine(MyString.substr(8,100));
System.Console.WriteLine(MyString.substr(8,-1));
System.Console.WriteLine();

// substring versus slice
System.Console.WriteLine("The method substring versus slice");
System.Console.WriteLine(MyString.substring(8,14) + "," +
  MyString.slice(8,14));
System.Console.WriteLine(MyString.substring(8) + "," +
  MyString.slice(8));
System.Console.WriteLine(MyString.substring(8,-1) + "," +
  MyString.slice(8,-1));
System.Console.WriteLine(MyString.substring(-5,8) + "," +
  MyString.slice(-5,8));

System.Console.WriteLine();

There are only a few remaining functions—the replacement functions and a few miscellaneous, useful functions. The replace function in JScript runs against the current String object. It replaces a regular expression pattern with a replacement string. The regular expression pattern can be inline, or it can be a regular expression object defined by JScript.


Note

Chapter 12, "Regular Expressions," discusses replacement strings and regular expressions.


The replacement string can contain replacement text or escape characters that can be used to include portions of the matched text in the replacement text. (You'll learn about the advanced portions of this parameter, including several ways of manipulating the replacement string, in Chapter 11. At this point, we will use very basic strings as replacements.)

The two remaining functions we'll explore here are toLowerCase and toUpperCase. These functions perform the roles their names suggest. The toLowerCase function returns a copy of the current string with all characters converted to their lowercase equivalents. The toUpperCase function returns a copy of the current string with all characters converted to their uppercase equivalents. Listing 3.7 uses the replace function with a simple case-sensitive set of regular expressions to match and perform replacements against strings that are modified with the toUpperCase and toLowerCase functions.

Listing 3.7 Basic JScript Regular Expressions

import System;

var reUpperCase = /MATCH/g;
var reLowerCase = /match/g;
var reNoCase = /MaTcH/ig;

var strCamelCase:String = "Where Is The Match In This String?";

// Let's try to Replace Match with Sasquatch in the string
Console.WriteLine(strCamelCase.replace(reUpperCase, "Sasquatch"));
Console.WriteLine(strCamelCase.replace(reLowerCase, "Sasquatch"));
Console.WriteLine(strCamelCase.replace(reNoCase, "Sasquatch"));
Console.WriteLine();

// Now let's make a lowercase version of the string and do it again
var strLowerCase:String = strCamelCase.toLowerCase();
Console.WriteLine(strLowerCase.replace(reUpperCase, "Sasquatch"));
Console.WriteLine(strLowerCase.replace(reLowerCase, "Sasquatch"));
Console.WriteLine(strLowerCase.replace(reNoCase, "Sasquatch"));
Console.WriteLine();

// And one final time, with the uppercase string
var strUpperCase:String = strCamelCase.toUpperCase();
Console.WriteLine(strUpperCase.replace(reUpperCase, "Sasquatch"));
Console.WriteLine(strUpperCase.replace(reLowerCase, "Sasquatch"));
Console.WriteLine(strUpperCase.replace(reNoCase, "Sasquatch"));

Console.WriteLine();

CLR String Instance Methods and Properties

This section covers all the instance methods and properties that you can use with a String object. It also discusses the set of methods and properties that you can use without an instance of a string.

Let's start by examining all the different ways you can get an instance of a String object. JScript allows you to create a string from a string literal in code. But the CLR can create strings from arrays of characters, from string literals, or from a single character repeated many times. These methods of creating String objects are best demonstrated via code. The following example uses several String object methods to create character arrays:

import System;

// Creation of a literal string
var LiteralString:String = "Hello World";
Console.WriteLine(LiteralString);

// Creation of a Character string created from the LiteralString
var CharString1:String = new System.String(LiteralString.ToCharArray());
Console.WriteLine(CharString1);

// Creation of a Character string using only parts of the LiteralString
// 0 is the offset to begin in the array, and 5 is the length.
var CharString2:String = new System.String(LiteralString.ToCharArray(), 0, 5);
Console.WriteLine(CharString2);

// Creation of a Character string by repeating a single character
// 20 means to repeat the character 20 times
var CharString3:String = new System.String('0', 20);
Console.WriteLine(CharString3);

This example converts a string literal to a character array and uses the character array to then create a String object. The method used in this example is simpler than creating a character array from scratch.

This example creates two "Hello World" strings, one "Hello" string, and a string of 20 zeros. Now that you have some String objects to play with, you can begin calling some of the methods and properties. To begin, you can get the length of any string in characters (not in bytes), by calling the Length property. No matter how the string is stored, you can always get the number of characters by using this property. You can then get individual characters from the String object by using the Chars property, which is an indexed property (that is, you can treat it just like an array). Simply passing in an array index retrieves a character. Make sure you use numbers in the range of 0 to Length -1, or you will get a runtime error, specifying that an invalid parameter was passed into the property. This is one of the many features of the strongly typed CLR datatypes.

All the string manipulation functions discussed earlier in the chapter are also made available here, in their CLR variants. The string constructors that use a character array to build a string and the Chars property are directly equivalent to the JScript character code functions.

The string search functions consist of the familiar IndexOf and LastIndexOf functions. Both of these functions can take a substring to search for, take a substring and a beginning offset, or take a substring with the beginning and ending offset. The major difference between the two functions lies in the ability to search for a single character and in being able to search for any given number of characters (for instance, a or b or c as opposed to the string "abc"). All versions of these functions—whether taking a substring, character, or array of characters—optionally accept a starting offset and an ending offset. If a string or character match is found, the offset within the string is returned; otherwise, -1 is returned. (This is the same behavior as in the JScript functions.)

The CLR offers the functions StartsWith and EndsWith, which are shortcuts for comparing substrings against the beginning or end of a given string. The return value is a Boolean true or false, depending on whether the string matched. However, these functions are often useful only after the string has been fully normalized (that is, any whitespace at the beginning or end of the string has been removed so that the string match can happen). To aid in removing extra characters, the CLR provides the trim functions. The trim function trims whitespace from the beginning and ending of a string, or it trims an array of characters from the string instead of trimming just whitespace. The trimStart and trimEnd functions take an array of characters or a null value to indicate whitespace. With this set of functions, you can trim any given number of characters or amount of whitespace from the beginning and end of a string.

The CLR also has padding functions. You can use the PadLeft and PadRight functions to add padding to each side of a string. To use either function, you simply pass in the length you want the string to be, and the function will pad the string with the needed spaces. You can pass an optional parameter in the form of a single character to specify padding with something other than a space. For example, later in this section, we'll be using an uppercase A instead of a space character to pad strings.

The CLR also provides the Substring function. Remember that JScript has three separate functions for getting substrings, each of which treats parameters in a different way. The CLR needs only one function for obtaining substrings, so if you want JScript-like functionality with multiple functions that do nearly the same thing, use the JScript functions. But be warned that they simply map back into the single CLR Substring function in the end. The Substring function takes just a starting index and returns up to the end of the string. It optionally takes a length parameter and returns the number of characters specified in the length parameter rather than returning to the end of the string. Note that if the starting index plus the length goes beyond the end of the string, the program throws a runtime exception. So in many cases, this function is not as nice as the JScript wrappers.

Listing 3.8 demonstrates all the functionality of the methods talked about so far.

Listing 3.8 CLR String Methods and Character Arrays

import System;

var Searchable:String = "This is our searchable string!";
var CharString:String = "aeiou";

// Demonstrates of the basic IndexOf and LastIndexOf functions
Console.WriteLine(Searchable.IndexOf("is"));
Console.WriteLine(Searchable.LastIndexOf("is"));
Console.WriteLine();

// Using Character Arrays. Will return vowels
Console.WriteLine(Searchable.IndexOf(CharString.ToCharArray()));
Console.WriteLine(Searchable.LastIndexOf(CharString.ToCharArray()));
Console.WriteLine();

// Advanced indexes for IndexOf and LastIndexOf functions
// These will return the second occurence of the string is from the
// beginning and from the end using the first search for the beginning
// offset of the second search.
Console.WriteLine(Searchable.IndexOf("is", Searchable.IndexOf("is") + 1));
Console.WriteLine(
  Searchable.LastIndexOf("is", Searchable.LastIndexOf("is") - 1)
);
Console.WriteLine();

// Set up variables for use with EndsWith and StartsWith
var NonPaddedString:String = "Match me in the beginning and the end.";
var PaddedString:String = "AAAAMatch me in the beginning and the end.AAAA";

Console.WriteLine(PaddedString.StartsWith("Match"));
Console.WriteLine(PaddedString.EndsWith("end."));
Console.WriteLine();

Console.WriteLine(NonPaddedString.StartsWith("Match"));
Console.WriteLine(NonPaddedString.EndsWith("end."));
Console.WriteLine();

// trim Some variables
var trimString:String = PaddedString.trim("A".ToCharArray());
var trimEndString:String = PaddedString.trimEnd("A".ToCharArray());
var trimStartString:String = PaddedString.trimStart("A".ToCharArray());

Console.WriteLine(trimString);
Console.WriteLine(trimEndString);
Console.WriteLine(trimStartString);
Console.WriteLine();

// Pad some variables
var PadLeftString:String =
  NonPaddedString.PadLeft(NonPaddedString.Length + 4, 'A');
var PadRightString:String = 
  NonPaddedString.PadRight(NonPaddedString.Length + 4, 'A');

Console.WriteLine(PadLeftString);
Console.WriteLine(PadRightString);
Console.WriteLine();

// And finally some Substrings
var Substring1:String = PaddedString.Substring(4);
var Substring2:String = PaddedString.Substring(4, PaddedString.Length - 8);

Console.WriteLine(Substring1);
Console.WriteLine(Substring2);

Console.WriteLine();

Another set of instance functionality that you should understand is the conversion functions, which behave identically to the JScript to* functions. The CLR maintains a ToUpper method and a ToLower method similar to the JScript equivalents. You've already seen the ToCharArray function, which allows you to convert a string into an array of its constituent characters. This comes in handy when you need to create character arrays from strings on-the-fly, as we've done several times in this chapter.

You also need a set of functions that work on the current string to add, remove, and insert substrings. The CLR has instance methods for removing and inserting strings, but it doesn't have one for appending or concatenating strings together. However, you can use the Insert method to make up for this shortcoming. The Insert method works on the current string and takes a starting index along with the substring to be inserted. You can use this function for appending a substring by setting the starting index to the very end of the current string. An associated Remove method takes the starting index and a length to specify a substring to be removed. Together, these two functions are capable of performing most string maintenance tasks.

You can compare any String object to any other String object by using the CompareTo method. The CompareTo method can also be used to compare any CLR objects.

The following is a fairly simple code example that shows how to do basic string manipulation by using the Insert and Remove methods

import System;

var TargetString:String = "Here is the final string!";
var StartString:String = "is the wrong stuff!";

// Lets go ahead and start the string off correctly
StartString = StartString.Insert(0, "Here ");
Console.WriteLine(StartString);
Console.WriteLine(TargetString.CompareTo(StartString));

// We need to get rid of that ending
StartString = StartString.Remove(12, 12);
Console.WriteLine(StartString);
Console.WriteLine(TargetString.CompareTo(StartString));

// Now lets finish this bad boy off
StartString = StartString.Insert(StartString.Length, "final string!");
Console.WriteLine(StartString);
Console.WriteLine(TargetString.CompareTo(StartString));

In this example, notice the results of the CompareTo method, which keeps working until the strings are equal. The value -1 means that the current string is less than the string being compared to, the value 1 means that the current string is greater than the string being compared to, and the result 0 means the two strings are equal.

CLR String Static Methods and Properties/Fields

There are only a couple helpful CLR string static methods and properties worth mentioning, so this section is short and to the point. There is only one field of note: the Empty field. The Empty field returns an empty string, and it is mainly used for comparing the current string against an empty string or for initializing string variables with nothing in them so that you can add to them later, using the string modification methods.

It can be useful to be able to print out a list of strings delimited with spaces, tabs, or commas. The Join method makes this pretty easy. With a separator string and an array of strings to join, the Join method returns a single string with all the strings in the array separated by the string.

What if you want to have a string returned in a special format? In this case, you need to use the format method of the String object. The format method enables you to pass in all sorts of formatting parameters, along with a number of objects. This method works on strings, numbers, and many other types of objects. The actual formatting string can be quite complex, but the general syntax is in the form of a string containing special formatting characters. These formatting characters begin and end with braces, in the form {numberofobjects, formatoptions}, where numberofobjects is the number of the object to format starting from 0, and formatoptions is a special set of formatting options that is passed to the object, allowing it to format itself (or not format itself, as the case may be).

Finally, the Compare method behaves identically to the CompareTo instance method. However, Compare lets you pass in the two strings to be compared rather than work against a string instance. This is handy when you need to compare two parameters to a function or compare command-line options. Listing 3.9 shows examples of formatting, joining, and comparing some numbers and converting them to strings. Numbers format very well and support quite a few format specifiers, so they work best when demonstrating the format function.

Listing 3.9 String Comparisons and formatting

import System;

// We need an array of Integers
var i:Int32 = 0;
var IntArray:Int32[] = new Int32[6];
var StringArray:String[] = new String[6];
var ObjectArray:Object[] = new Object[6];

IntArray[0] = 1;
IntArray[1] = -1;
IntArray[2] = 2;
IntArray[3] = -2;
IntArray[4] = 100000;
IntArray[5] = -100000;

// Let's join some Strings together
for(i = 0; i < 6; i++){
  // Lets cast to a string
  StringArray[i] = String(IntArray[i]);

  // And an Object
  ObjectArray[i] = Object(IntArray[i]);
}
Console.WriteLine(String.Join("|sep|", StringArray));
Console.WriteLine();

// Let's compare some strings together
for(i = 0; i < 6; i++,i++){
  // Print out the results of string comparisons
  Console.WriteLine(String.Compare(StringArray[i], StringArray[i+1]));
  Console.WriteLine(String.Compare(StringArray[i], StringArray[i]));
}
Console.WriteLine();

// Let's finish up with the formatting
var formatString:String =
 "Hex the number\n{0:X}\n" +
 "Left align 20 characters\n{4,-20}\n" +
 "Right align 20 characters\n{5,20}\n";

Console.WriteLine(String.format(formatString, ObjectArray));

Console.WriteLine();

Using StringBuilder Instead of String

Now that we have explored the greatness of the String object, let's talk about what isn't so great about it. The problem with a String object is that every time it is modified, a brand new string is created. This happens so that any piece of a program that is working on or examining the older version of the string doesn't have it magically change. It's a nice feature of the CLR to make strings immutable and thread-safe.

It is fairly obvious that any process that concatenates many values to a single string, or works on very large strings with even a few items being appended to the end, needs more performance than does a process that copies the old String object with any new changes every time a modification is made. The CLR StringBuilder class is exactly what you need in this case. The StringBuilder class resides in the System.Text namespace and has only a few methods and properties that are of interest to us.

To add to the current string, you use the StringBuilder class's Append method. This method is overloaded and can take a piece of a string, an integer value, or even a Boolean variable. The Appendformat method allows you to specify a formatting string along with the variables to be formatted. This is similar to the format string that is used elsewhere in the book with the Console methods. For more information on formatting, take a look at the .NET framework SDK documentation.

You can insert and remove values from a string. The Insert method takes a character offset in the string and begins placing either a string you've passed in or another value. The Insert method doesn't have a formatted version, so you have to do any formatting of the string beforehand. The Remove method takes both a starting index and a length for the string to be removed.

Append also includes some utility methods. The replace methods replace a given string, integer, or character with another. These are highly specialized and extremely quick replace functions. A more powerful alternative would be to use regular expressions, but for simple character replacement, the replace methods are fastest. Another important utility function is ToString(). Though this has to be exported by every object in the CLR, its use here is extremely important because it is the only way of turning a StringBuilder into a string that you can use elsewhere in an application.

Some of the properties of the StringBuilder object are also pretty neat. Each StringBuilder object has a Capacity property and a MaxCapacity property, which control how much space is allocated for the current string and how much space can be allocated as the string grows, respectively. If you know a string is going to grow to a certain size, it can be extremely helpful to set the capacity in the beginning. This prevents the StringBuilder object from having to grow as you expand the string and enables you to preallocate the space needed. You can pass an indexed property, Chars, the index of a character to return. It behaves like the charAt function of the JScript string. Finally, you can use the Length property to find the number of characters currently loaded into the StringBuilder object.

Listing 3.10 shows how to use the StringBuilder object.

Listing 3.10 Using StringBuilder to Create Strings

import System;
import System.Text;

BuildString();

function BuildString() {
  var MySB:StringBuilder = new StringBuilder();

  // Appending normal text
  MySB.Append("Lets start with this\nLittle bit of text\n\n");

  // Appending formatted text
  MySB.Append("Here we are formatting some numbers\n");
  MySB.Appendformat("{0:X}, {1,10}, {2}\n\n", 500, 300, 10);

  // Using the Insert method
  MySB.Insert(0, "This text is getting inserted\n\n");

  // Removing some text
  MySB.Insert(0, "This is getting inserted to be removed\n");
  MySB.Remove(0, "This is getting inserted to be removed\n".Length);

  // Let's make numbers a bit bigger!
  MySB = MySB.Replace("numbers", "NUMBERS");

  // And Finally lets examine the Properties
  MySB.Appendformat("Capacity:   {0}\n", MySB.Capacity);

  // Let's set a higher capacity
  MySB.Capacity = MySB.MaxCapacity / 100;

  MySB.Appendformat("New Capacity: {0}\n", MySB.Capacity);
  MySB.Appendformat("Max Capacity: {0}\n", MySB.MaxCapacity);
  MySB.Appendformat("Length:    {0}\n", MySB.Length);
  MySB.Appendformat("Some Chars:  {0} {1} {2}\n",
    MySB.Chars(0),
    MySB.Chars(1),
    MySB.Chars(2));

  // Notice the use of ToString()!
  Console.WriteLine(MySB.ToString());

}

Summary

In this chapter you have learned about all the basic datatypes available in JScript .NET, along with quite a few of the object types that are available through the CLR. Services are provided in two locations in JScript .NET: through the JScript .NET language and through the CLR, which hosts the JScript .NET language while it is running. You'll make use of both of these services throughout the book because they are both important to effective .NET programming.

New performance-oriented options are available in JScript .NET. For example, if you strongly type code, compile-time warnings appear if a variable assignment is inappropriate. You can also use bounded arrays through the CLR, as opposed to the JScript .NET dynamic arrays. Bounded arrays offer more performance than dynamic arrays, but dynamic arrays offer more functionality and are more convenient.

In this chapter you learned how to use strings and how the new StringBuilder class can provide great memory usage and speed improvements when you're building long strings from many smaller parts.

In Chapter 4, "Operators and Commenting," you'll learn about the various operators available in JScript .NET, including the arithmetic, assignment, comparison, Boolean, and bitwise operators. You'll also learn about some of the object-oriented operators that allow you to create new instances of objects and find out what type of object you're working with.