Expressions, variables, and constants are some of the most basic building blocks of computer programs, and you’ll use them all extensively in your ASP.NET applications.
Expressions are central to virtually all computer programs. Expressions enable you to:
Compare values to one another
Perform calculations
Manipulate text values
An expression can be as simple as the following:
1 + 1
An expression like this isn’t very useful by itself, however. Unlike people, who can easily recognize “one plus one” and fill in the blank (“equals two”), computers aren’t capable of that kind of leap of logic. For the expression to be useful, it needs to tell a computer not just to add one and one, but to store the result somewhere so that we can make use of it later (either by displaying it to the user or using it in another expression later). This is where variables come in.
As with the preceding example, at some point during the execution of most programs, you’ll need to store values of some sort. Examples include the results of mathematical operations (as in the preceding example), text input from users, and the results of comparisons. Simply put, variables are storage areas for data. This data can be numeric, text, or any one of a number of special data types.
The data type variable defines the type of data that can be stored in it, as well as the format in which that data is stored (and the amount of memory that the system needs to allocate for the variable). The following table lists the data types supported by C# and Visual Basic .NET, as well as the Microsoft .NET Framework SDK types to which they map. The data types marked with an asterisk don’t have a native representation, but you can still access these data types by using the appropriate System type when declaring the variable. Why cover Visual Basic .NET types? If you are creating a class to be used by other developers, knowing how the types match between C# and Visual Basic .NET is critical, both to ensure that the correct type is used, and to document the class in a Visual Basic .NET programmer-friendly way. The good news is that, unlike in the 32 bit Win32 world of Visual Basic 6 and Visual C++ 6, an integer in both environments is the same size and has the same numeric range.
The data type of a variable is determined at the time the variable is declared. This will be discussed further in the section titled “Declaring Variables” later in this chapter. Some data types (such as the Date type) do not have language specific implementations. In Table 3-1, there is an asterisk (*) in the cells of types without the corresponding language-specific data types. Some data types are not Common Language Specification (CLS) compliant, and are noted with a cross (†) in the .NET data type column.
C# data type |
VB.NET data type |
.NET data type |
Size |
---|---|---|---|
bool |
Boolean |
System.Boolean |
1 byte |
byte |
Byte |
System.Byte |
1 byte |
sbyte |
* |
System.SByte † |
1 byte |
char |
Char |
System.Char |
2 bytes |
* |
Date |
System.DateTime |
8 bytes |
decimal |
Decimal |
System.Decimal |
12 bytes |
double |
Double |
System.Double |
8 bytes |
int |
Integer |
System.Int32 |
4 bytes |
uint |
* |
System.UInt32 † |
4 bytes |
long |
Long |
System.Int64 |
8 bytes |
ulong |
* |
System.UInt64 † |
8 bytes |
object |
Object |
System.Object |
4 bytes |
short |
Short |
System.Int16 |
2 bytes |
ushort |
* |
System.UInt16 † |
2 bytes |
float |
Single |
System.Single |
4 bytes |
string |
String |
System.String |
10 bytes, plus (2 x string length) |
struct |
User-Defined Type |
System.ValueType (inherited) |
Sum of member sizes |
Important |
ASP.NET contains both framework-specific data types and language- specific data types for specific .NET languages, such as C# and Visual Basic .NET. To take advantage of some of the multilanguage features of the .NET environment, you need to limit your use of data types to those supported by the CLS, a subset of the data types supported by the common language runtime. In the preceding table, data types marked with a † are not CLS-compliant. Avoid using them in classes that you want to make available for use with other .NET languages. Note that C# does not have a native type that maps to the DateTime data type, but using the .NET data type is just as easy. |
In .NET development, there are two categories of data types: value types and reference types. Although understanding the distinction between these types isn’t absolutely necessary if you want to develop ASP.NET applications, it can help you better understand how these data types operate and how to deal with error messages resulting from coding errors. (Not that those ever happen, right?)
Value types are data types that store their data directly as values in memory. They include all the numeric types (int32, short, single, and so on), structures (custom data types based on System.ValueType), and enumerations (custom types that represent a defined set of values), as well as boolean, char, and date types. Value types are accessed directly, as in the following example:
int myInt; // C# declaration for an int3 // Expression to assign the value 123 directly to the variabl myInt = 123;
Note |
C++ programmers will recognize all the syntax used to declare and assign variables, as well as comment delimiters. If instead you are using C# and have a Visual Basic background, several elements of C# syntax might require explanation. Virtually all C# statements end with semicolons (;). Multiple C# statements can appear on a single line as long as all statements are terminated with semicolons. Unlike Visual Basic .NET, you can spread a single statement over as many lines as needed without using any sort of line extender (such as the underscore character (_) used by Visual Basic .NET). C# ignores whitespace characters, such as spaces, tabs, and new lines. Comments in C# can be single line comments that begin with two slash characters (//) or multiline comments that begin with slash-star (/*) and end with star-slash (*/). |
Reference types are data types that store a reference to another memory location that contains the data, which is usually based on a class, such as the String class in the .NET Framework. Reference types include Object (which acts as a replacement for Visual Basic’s Variant type, no longer supported in .NET), String, and all Arrays, as well as instances of custom classes. Reference types are accessed through members of the class to which the type holds a reference, as in the following example:
// Define a clas class myClass public int myInt // declare an instance of the clas myClass myClassInstance // Allocate space for the clas myClass = new myClass() // Expression to assign the value 123 to myClass member myIn myClass.myInt = 123;
Unlike value types, in which all you need to do is declare the variable and set a value, reference types must be declared and then have space allocated for them, using the new keyword in the example.
Note |
Conversion between value types and reference types is accomplished through a process called boxing and unboxing. Boxing is the process of creating an instance of the reference type object and assigning the value of a value type to the object (as well as storing information on the value’s data type). Unboxing is the process of assigning the value of the boxed object type to a variable of the appropriate type (or a compatible type, such as assigning a boxed int16 to a variable of type int32). Boxing and unboxing variables enables you to use the most lightweight variable possible, while still being able to box that variable into an object. An example follows: int myInt = 42; // declare and set value of variable |
Before you can use variables in your programs, you need to declare them. Variable declaration is the process of specifying the characteristics of the variable (data type, lifetime, scope, and visibility) so that the runtime system knows how much storage space to allocate for the variable, which actions to allow on the variable, and who can take those actions. A variable declaration takes
the following form in C#:
int x; // Declares a variable of type int
Note |
The previous example uses x as the name of the variable. Although this is perfectly acceptable as far as the language compilers are concerned, you should consider giving your variables more meaningful names, such as FirstName (for a string variable holding a first name), LoopCount (for a numeric variable used in a looping structure), or Person (for a reference to a class representing data on a person). This naming convention makes it much easier to remember the purpose of a variable, and it will make your code much easier to maintain. You can find naming guidelines for use in .NET class libraries at http://msdn.microsoft.com/library/en-us/cpgenref/html/cpconnetframeworkdesignguidelines.asp. |
The preceding declaration specifies a variable of type int (which maps to the System.Int32 type). In addition to simple declarations like these, you can use the placement of the declarations, as well as C# keywords, to modify the lifetime, scope, and accessibility of your variables.
Lifetime refers to the span of time from when the variable is declared to when it is destroyed. The lifetime of a variable depends on where it is declared.
Note |
The following examples in this chapter write text into the Text property of a label control named Message. Labels and other server controls will be explained more completely in Chapter 8. |
For example, the lifetime of a variable declared inside a procedure is limited to the execution of the procedure. After the procedure has finished executing, the variable is destroyed and the memory it occupied is reclaimed. An example is shown here:
// C# metho private void HelloWorld( String HelloString string hellostring HelloString = "Hello World!" hellostring = HelloString Message.Text += HelloString Message.Text += "<br/>" Message.Text += hellostring // Both HelloString and hellostring will disappea // after the next line.. }
Important |
There are two strings in this example, one declared as a String and the other declared as a string to remind you that C#, like C++, is case sensitive. In this example, the first string, HelloString, is declared as a System.String because in searching for the type String (note the uppercase S), C# will find System.String. The second variable, hellostring, is declared as a string (note the lowercase s). This means that hellostring is declared using the C# alias for System.String. C# considers HelloString and hellostring to be entirely different objects. This is an important point to remember if you are a beginning C# programmer. |
When this procedure completes, the variables HelloString and hellostring no longer exist. Their lifetime has ended. If HelloString had been declared outside the procedure, as in this next fragment of code, its lifetime would be until the instance of the class containing it was destroyed.
// Declare string outside of metho String HelloString // C# metho void HelloWorld( HelloString = "Hello World!" Message.Text += HelloString // HelloString will still exis // after the next line.. }
C# variables can also be declared as static. Static variables exist for the life of the class, and even if there are many instances of the class, there is only a single instance of the static variable. Static variables must be declared as members of the class rather than inside a single method.
Note |
The C# documentation recommends using whole words when naming your variables. You should use mixed case, with the first letter of each word capitalized, as in HelloString. This is called Pascal case. This is one of many possible naming conventions you can use for variables and other elements in your programs. Naming conventions are simply agreed-upon standards for how elements will be named in a program. For instance, making the first character of the first word in a phrase lower case and capitalizing the first character of each subsequent word is called Camel case. The particular naming convention you choose isn’t important, but you must choose one and use it consistently. Doing so will make it easier for you to maintain your code and will help others understand what it’s doing. |
Scope, also known as visibility, refers to the region of code in which a variable can be accessed. The scope of a variable depends on where the variable is declared, and in Visual C# .NET it can include the following levels.
Block-level Variables declared within curly braces, {}, for instance in an if or while statement, are referred to as block-level variables. Although the scope of block-level variables is limited to the block in which they’re declared, their lifetime is that of the procedure in which the block appears.
Procedure-level Also known as local variables, these are visible only within the procedure in which they’re declared.
Module-level In modules, classes, or structures, any variable declared outside of a procedure is referred to as a module-level variable. The accessibility of module-level variables is determined by any accessibility keywords used in their declaration (see the section titled “Accessibility” later in this chapter).
Namespace-level Variables declared at module level but given public accessibility (public or internal), are referred to as namespace-level variables. They’re available to any procedure in the same namespace that contains the module in which the variable is declared.
There is one significant difference in how scope works in C# vs. C++. The following code would work in C++, but fail in C#:
private void ScopeTest( int foo foo = 2 if ( foo != 2 // This would work in C++, fail in C int foo foo = 7 // In C++, foo would now be equal to the number 2, and th //foo declared inside the if statement is not visibl //or available /* In C#, if the only instance of foo was declared insid the if statement, foo would not be visible o available, but you would still be unable t declare a variable named foo * }
In this contrived example, even though the second copy of foo is declared within the braces of the if statement, the compiler will generate an error and fail to compile the code because that second instance of foo would hide the first instance. In C++, the second declaration of foo would declare a variable visible only inside the if statement, hiding the previous value of foo. You can, however, declare a variable at the class level and then declare a variable of the same name, even with a different type, within a method of that class.
There are a couple of guidelines to follow regarding lifetime and scope:
Limit your variables to the narrowest possible scope that will allow you to accomplish your objectives.
Avoid using the same name for variables of different scope within the same module even where it is allowed. Using the same name in such situations can lead to confusion and errors.
Because module-level and static variables all continue to consume memory as long as the class or module is running, you should use them only when necessary.
The last important concept in variable declaration is accessibility. The accessibility of a variable determines whether it can be accessed from outside the module (or application) in which it’s declared. In C#, accessibility is determined through the use of one the following modifiers.
public Variables declared with the public keyword are accessible from anywhere in the same module (or class) in which they’re declared, the namespace containing that module, and any other applications that refer to the application in which they’re declared. They can only be used at the module or class level.
internal Variables declared with the internal keyword are accessible from anywhere in the same module (or class) in which they’re declared, as well as the namespace containing that module.
protected Variables declared with the protected keyword are accessible only within the class in which they’re declared or a class that inherits from that class. (See the section titled “Using Inheritance” later in this chapter.) The protected keyword can be combined with the internal keyword and can be used only to declare class members.
private Variables declared with the private keyword are accessible only from the module, class, or structure in which they’re declared. In classes or modules, declaring a variable with no modifier or as private has the same effect on the variable’s accessibility, but private makes your intentions more explicit. The private keyword cannot be used within a procedure.
Note |
In C#, all variables must be declared before they can be used. Failure to declare a variable will result in a compiler error. In Visual Basic .NET and some other languages, it’s possible (but not advisable) to use variables without first declaring them. |
In addition to the normal operators you are familiar with (‘+’ for addition, ‘-’ for subtraction, ‘/’ for division, ‘*’ for multiplication, and ‘=’ for assignment), there are several operators borrowed from C and C++ that require some explanation.
++ Increment operator, incrementing the value by 1 (i++ is the same as i=i+1)
— Decrement operator, decrementing the value by 1 (i— is the same as i=i-1)
+= Increment assignment operator (i+=5 is the same as i=i+5)
-= Decrement assignment operator (i-=5 is the same as i=i-5)
== and != Equality and Inequality operator
% Modulus (remainder) operator. (17%5 equals 2)
There are other, less essential operators that are fully described in the MSDN documentation, and even the common operators can have other meanings. For instance, the plus sign (+) is also used as a string concatenation operator. In addition, operators can be overloaded, enabling you to define new meanings for them when used in combination with your class.
Constants are similar to variables, except for one important detail: After a constant has been declared and initialized, its value cannot be modified. Constants are very useful when you have a literal value that you want to refer to by name. For example, if you’re a fan of Douglas Adams, you might want to create a constant to refer to the literal value 42.
const int TheAnswer = 42;
Anywhere within your program (subject to the same scope, lifetime, and accessibility rules as variables) you can use the constant name TheAnswer instead of the literal value 42.
Constants are particularly handy in place of literal values used as the arguments to methods of components you might use. For example, in classic Microsoft ActiveX Data Objects (ADO), the data types of stored procedure parameters were represented by numeric values. Trying to remember all those values would be unrealistic, so the developers of ADO also made it possible to use constants (adInteger, adVarChar, and so on) in place of the literal values.
In other words, constants let you substitute easy-to-remember names for difficult- to-remember literal values. This can make your code easier to write and maintain.
Many constants used with ASP.NET are actually created using enumerations. Using enumerations, you can locate all related constants together, and the enumerations can be used in a type-safe way. For example, enumerations would be handy if you have a method that accepts numeric values representing a color numbered between 1 and 3. For instance, you might want to set a color:
private void SetColor(int color);
This method will, in fact, happily accept any integer value. You could declare an enumeration as follows:
enum ColorType Red = 1 Green Blu }
By default the first element of an enumeration is equal to 0. In this example, because I need an enumeration that contains 1 through 3, I set the first value in the enumeration to 1, and then the subsequent values are each incremented by one. So Green is 2 and Blue is 3. Now, rather than the previous method declaration, you could use the following:
private void SetColor(ColorType color);
This method will require an instance of the ColorType enumeration passed as the parameter, ensuring that the value is a valid member of the enumeration, and thus that the value represents a valid color. The following is an example of calling SetColor with a ColorType enumeration member:
SetColor(ColorType.Blue);