Types in C: An Overview


Types in C#: An Overview

Before we begin our discussion of the various types found in C#, we need to clarify what a type is and its significance in C#.

What Is a Type?

In our everyday language, the word type usually signifies a number of persons or items sharing one or more particular characteristics. This often causes a group of people of the same type to be regarded as a more or less precisely defined group.

In C#, all values belonging to a specific type can be said to share a set of predefined characteristics.

Important characteristics for a variable of a simple type are the values that can be represented, its internal memory requirements, and its predefined behavior when applied in various computations. For example, each variable of type int can store integers of the size 2147483648 to 2147483647, and will occupy 32 bits of internal memory.



Recall from Chapter 1, "Computers and Computer Programming: Basic Concepts," how computer software has the ability to interpret a single bit or a combination of several bits of computer memory in many different ways. By specifying a variable to be of type int, we are instructing the program to interpret the underlying bits as representing a value of type int.

Further, a clearly specified set of operations can be performed on a variable of type int. For example, it would not make sense to perform arithmetic calculations on a value of type string, whereas this would be perfectly natural with a value of type int. C# supports this logic through operator overloading, as demonstrated in Chapter 4. When the + operator combines two operands of type int, C# performs standard arithmetic addition, whereas two operands of type string are concatenated.

C# contains several other pre-defined overloaded operators and it is possible to extend these built-in features by specifying your own user-defined overloading processes.

By understanding the type concept, we can begin to glimpse the power of types in C#. The next sections will elaborate on this power.

C#: A Strongly Typed Language

Every value in C# must have a type. Due to the wealth of types found in C#, you will often need to combine values of different types in the same statement of a source program. To help the programmer apply this range of types correctly, speed up development time, and increase the robustness of the finished application, C# contains succinctly defined rules for the compatibility between various types when they are mixed. The compiler acts as the police; it reinforces these rules to the best of its ability, by reporting errors and warnings.

The procedure performed by the C# compiler to ensure that the compatibility rules of a program are adhered to is called type checking.

The following three examples illustrate typical circumstances under which the C# compiler attempts to ensure that values of different types work together smoothly:

  1. An Assignment statement;

  2. A method call passing arguments to the called method;

  3. An expression.

    • An assignment statement Whenever a value is assigned to a variable in an assignment statement, the compiler checks the compatibility between the type of the variable and the type of the value assigned to it; if non-compatible, you will bump into an error message.

      Let's illustrate. You could attempt to write the following lines of code


      but the second line would result in a compiler error, due to incompatible types.

A breach of the type compatibility rules is termed a type clash.

  • Method call passing arguments Passing arguments to formal parameters during method invocations is another area of potential type clashes and, consequently, is scrutinized by the compiler. The following example would result in an error during compilation:


    The error is caused by a type clash between the arguments and the formal parameters. The method call to Product contains arguments of type string, but the formal parameters of Product's method header expect to receive arguments of type int.

  • Expressions Any expression in C# has a specific type. When expressions are nested inside each other, the compiler chooses the type logically suited for expressions at higher levels, while simultaneously checking that these choices are sound. Take a look at the following code snippet:

     01:  string text1; 02:  string text2; 03:  int number1; 04:  int number2; 05:  int result; 06:  number1 = 5; 07:  number2 = 14; 08:  text1 = " If you want to C#'er, "; 09:  text2 = "go to the optometrist"; 10:  result = (number1 + number2) + (text1 + text2); 

    Let's zoom in on line 10 for a moment, as shown in Figure 6.1.

    Figure 6.1. A nested expression.

Line 10 contains a few nested expressions. Recall that any literal or variable is an expression, so number1, number2, text1, and text2 are all expressions in their own right. number1 and number2 are both of type int and, when combined with the + operator, they form a new expression (number1 + number2), which is also of type int. The expression (text1 + text2) is formed to be of type string. When (number1 + number2) is combined with (text1 + text2) by using the + operator, the compiler faces a dilemma. How can it add an expression of type int to an expression of type string? The only way out is to implicitly convert (number1 + number2) to type string, which can then be concatenated with (text1 + text2).

However, result on the left side of the equality operator = is of type int and incompatible with string, which finally triggers a compiler error.

The statement would have been valid if result had been of type string. result would then have received the value "19 If you want to C#'er, go to the optometrist".

Implicit conversions are conversions from one type to another that are performed automatically by the C# compiler.

For an implicit conversion to take place, it must not incur any loss of data and must not require any further need for a detailed examination of the particular values involved.

A computer language that attempts to prohibit the use of any value in any process of which is not meant to participate, is termed a strongly typed computer language.

C# is a strongly typed language.

All expressions and statements in C# are, where relevant, checked for compatibilities between the involved values, in a fashion similar to the examples previously provided.

The Advantages of Types

Why should we as programmers appreciate the type system found in C#? Considering what I have mentioned so far about types in C#, we can appreciate the extreme power and high information content of the following declaration statement:

 int distance; 

With this statement, we tell the C# compiler the following:

  • We only want to store whole numbers in distance.

  • We only want to store numbers of a certain magnitude in distance.

  • The program needs to allocate only a limited amount of memory to hold a value in distance.

  • Whenever we, for example, use the + symbol in connection with distance, the program implicitly assumes that we want to perform an addition operation. This holds for all other arithmetic operators (multiplication, subtraction, and so on).

  • If we try to assign this variable to a variable of an incompatible type, it is probably because we have made a mistake, so the compiler will direct our attention to the problem by returning an error or warning message.

Yes, an impressive amount of information; all contained in one small, unpretentious statement. In fact, our ability to use types and write declaration statements such as the one previously shown allows us to let the compiler take over many tedious chores. This can significantly reduce the amount of source code we have to write.

The Power of Declarative Information


Consider the following two statements:


The declaration statement (1) consists of declarative information, which tells the computer what to do. The assignment statement (2) tells the computer how to do something. As you have already seen, the declaration statement is extremely powerful and contains a lot more information than the assignment statement (2), which merely tells the computer how to calculate sum.

In general, telling someone (including the computer) what to do (do the dishes, for example) is a lot easier than telling him or her (it) how to do it (stopper the drain, open the tap, get the dishwashing liquid, take the first plate, and so on).

C# includes an exciting feature called attributes that allows programmers to expand and custom design the declarative information he or she can communicate to the computer. Attributes are discussed further in Chapter 21, "Preprocessing, XML Documentation and Attributes."

The Types of C#

You have now seen how the types of C# can be divided into simple types and derived types. Another equally important, and certainly more correct, way to group the types of C# is according to whether a type is a value type or a reference type.

Value Types and Reference Types

The distinction between simple types and derived types is, strictly speaking, not correct. C# uses a class-like value type, called a struct, to represent its simple types. This allows every simple type to have pre-defined methods attached to it. For this reason, it is possible to call the predefined method ToString of 5 by writing


which returns the string "5". In many ways, what we have defined in the previous section as a simple type is just as complex as many derived types.



ToString is a commonly used method name in the .NET Framework. It is, as its name implies, used to return a value of type string from a value. The string often describes core information about this value. You can locate the ToString method of 5, used in the example, in the .NET Framework documentation by looking at the methods of the System.Int32 struct.

Nonetheless, the distinction between simple types and derived types is convenient and originates from previous languages, such as C++ and Java, which do not contain C#'s sophisticated simple types. These languages contain true simple types, meaning a number is a number without any extra functionality attached. Later, when we return to a more detailed discussion of the struct mechanism, we will see why C# can be regarded as a fully object-oriented programming language where everything, including the simple types, can be viewed as being an object.

A variable of a value type holds the actual value stored in it. If we could zoom in on the actual value kept in the memory, it would store this particular value here. The int type is a typical example of a value type. If we declared a variable of type int called myNumber and assigned it the literal 345, we could depict it as shown in Figure 6.2.

Figure 6.2. int is a value type.

All this is fairly straightforward and probably not surprising to you, but it is worth keeping in mind when we look at the other major set of types, the reference types.

A variable of a reference type contains a reference to an object in the computer memory and so does not, itself, contain the object. The reference consists of the location (or address) of the referred to object. To illustrate what a reference type is, we need first to turn our attention to our old friend the string type.

Hmm…our otherwise trusty string type actually turns out to have a hidden agenda; here is one important revelation about it: string is a reference type.

A variable of type string does not itself contain a string but is declared to hold a reference to a string located somewhere else in the computer memory.

Consider the following line of code:


This declaration statement actually says, "Allow myText to hold a reference to a string."

We can now use myText in the following assignment statement:

 myText = "Lets go to the C to catch a #"; 

which assigns the address of the string "Lets go to the C to catch a #" to myText.

If you tried to zoom in on the underlying memory of myText, you would not find any text, just a memory address, also called a reference. This is illustrated in Figure 6.3, where I have arbitrarily located the text at the address 4027, which is the address held by myText.

Figure 6.3. string is a reference type.



Fortunately, you never see the actual address when using references in your source code.

The string class is a good example of a reference type, but we could also have used our Elevator or Person classes from Listing 5.1 in Chapter 5 as examples of reference types.



All classes are reference types.

The memory address referring to where an object is stored in the computer memory is termed a reference to that object.

Under many circumstances, the differences between working with value types and reference types are negligible. Our ability to work with string in the previous chapters without understanding the reference concept is an example of this fact. However, there are marked differences in other cases in the behavior of the two kinds of types, of which the object-oriented programmer must be aware. The source code in Listing 6.1 illustrates a typical scenario of this kind.

Listing 6.1 Source Code of ReferenceTest.cs
01: using System; 02: 03: /* 04:  * This class gives an example of 05:  * a difference between reference 06:  * types and value types 07:  */ 08: 09: class Person 10: { 11:     private int age = 0; 12: 13:     public void SetAge(int newAge) 14:     { 15:         age = newAge; 16:     } 17: 18:     public int GetAge() 19:     { 20:         return age; 21:     } 22: } 23:  24: class ReferenceTester 25: { 26:     public static void Main() 27:     { 28:         Person julian; 29:         Person deborah; 30: 31:         julian = new Person(); 32:         deborah = new Person(); 33:         julian.SetAge(2); 34:         deborah.SetAge(33); 35:         Console.WriteLine("Julian's age: " + julian.GetAge()); 36:         Console.WriteLine("Deborah's age: " + deborah.GetAge()); 37:         julian = deborah; 38:         Console.WriteLine("Julian's age: " + julian.GetAge()); 39:         Console.WriteLine("Deborah's age: " + deborah.GetAge()); 40:         julian.SetAge(10); 41:         Console.WriteLine("Julian's age: " + julian.GetAge()); 42:         Console.WriteLine("Deborah's age: " + deborah.GetAge()); 43:     } 44: } Julian's age: 2 Deborah's age: 33 Julian's age: 33 Deborah's age: 33 Julian's age: 10 Deborah's age: 10 

Line 9 defines a simple class called Person that contains just one instance variable representing age (line 11).

In lines 13 21, a couple of methods called SetAge and GetAge permits access to the value of age.

The class ReferenceTester holds the Main method, which declares two variables (julian and deborah in lines 28 and 29) to hold references to objects of class Person.

Lines 31 and 32 each create a new instance of the Person class with the new keyword. Here comes the first important point: Line 31 assigns a reference pointing at the newly created Person object to the julian variable. Line 32 assigns another reference, this time to the deborah variable. This reference points to the new Person object created in this line, which is different to the Person object created in line 31.

In lines 33 and 34, the age instance variables of the two objects are given the values 2 and 33 respectively. Thus, after line 34, we can illustrate the references from julian and deborah as in Figure 6.4.

Figure 6.4. References after line 34 of Listing 6.1.

Lines 35 and 36 print out the ages of julian and deborah.

In line 37, the program assigns the value of deborah to the julian variable. However, because this value is a reference type, we are merely telling julian to reference the same object as deborah, leading to the situation displayed in Figure 6.5.

Figure 6.5. References after line 37 of Listing 6.1.

Thus, julian and deborah are now referencing exactly the same object. This is partly confirmed by the output provided by lines 38 and 39 where both julian and deborah are shown to be the same age 33. However, the convincing and often surprising result is found through lines 40 42. The program changes the age of julian to 10 in line 40 but, according to the sample output, it also changes the age of deborah to 10, confirming that julian and deborah are, in fact, pointing at the same object.

It is important to be aware of the differences between value types and reference types. Not only will this allow you to pinpoint and fix problems in your source code more quickly, but it also allows you to create speedier, more sophisticated programs.

The Main Types of C#: An Overview

Equipped with the knowledge of value types and reference types, it is useful to look at an outline of the main types found in C#, as shown in Table 6.1. Even though we have only discussed a few of the types mentioned in Table 6.1 so far, they are shown at this point of the book as an overview and a reference when you continue through the book.

Table 6.1. Overview of the Main Types in C#
Value Types Reference Types
Simple types (int is an example) Class types (Elevator and Person from Listing 5.1 are examples string type
Enum types[*] Array types[*]
Struct types[*]

Interface types[*]

Delegate types[*]

[*] not yet discussed

The following are some brief explanations of the types available in C#:

  • Simple types The now familiar int type is just one of the 13 simple types found in C#. The simple types are used to hold numeric values and single characters. They are discussed thoroughly in the next section of this chapter.

  • Enum types These types provide the means for creating symbolic constants, especially for sets of related constants, such as the days of the week (Monday, Tuesday, and so on), the months of the year (January, February, March, and so on), or a set of colors (Green, Red, Blue).

  • Struct types This type contains methods and data just like classes. Even though they are similar to classes, they also have a few important differences. One of the most important differences is that class types are reference types, whereas structures are value types. All simple types are struct types.

  • Class types A class defines a category of objects and is a blueprint used for creating objects. Classes contain members that, among others, can be instance variables that describe the state of an object and methods that specify the behavior of an object. Classes can inherit members from other classes. The class concept is pivotal to object-oriented programming.

  • Array type Variables of type array are objects dedicated to storing small or large collections of data. Each data item in an array must be of the same type. For example, if you wanted to represent the daily rainfall in London over the last 100 days, you would likely use an array.

  • Interface types An interface stipulates abstract behavior by specifying one or more method headers but without the accompanying implementation that is found with non-abstract methods in a class. Classes can implement interfaces and must concretize the abstract behavior stated by the interface by defining the implementation. Interfaces allow the programmer to implement advanced object-oriented concepts.

  • Delegate types Like interfaces, delegates are used to specify behavior, but they only specify the method header for a single method. An instance of a delegate type contains one method, and the delegate constitutes a reference to this method. The delegates (or method references) can be passed around in a program and be executed. Delegates are vital for implementing event-driven programs in C#.

The .NET Common Type System (CTS)


The .NET Common Type System (CTS) is an integral part of the .NET Framework. It defines all the types described in this section and contains the rules for how applications running under the .NET runtime can apply these types. As its name implies, all programming languages that target the .NET platform, including C#, are based on the types defined by the CTS.


C# Primer Plus
C Primer Plus (5th Edition)
ISBN: 0672326965
EAN: 2147483647
Year: 2000
Pages: 286
Authors: Stephen Prata

Similar book on Amazon

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