The C# Station Tutorial

by Joe Mayo - csharp-station.com
created 07/04/08, updated 1/12/09, 9/1/11

Lesson 22: Topics on C# Type

Throughout this tutorial, you've seen many different types, including those that are part of C# and custom designed types. If you've taken the samples and worked on them yourself, extending and writing your own programs, you are likely to have experienced errors associated with type. For example, you can't assign a double to an int without using a cast operator to perform the conversion. Another feature of C# concerns the semantic differences between reference and value types. Such problems should make you wonder why this is so and that's what this lesson is for. Here are the objectives for this lesson:

  • Understand the need for type safety
  • See how to convert one type to another
  • Learn about reference types
  • Learn about value types
  • Comprehend the semantic differences between reference and value types

Why Type Safety?

In untyped languages, such as scripting languages, you can assign one variable to another and the compiler/interpreter will use an intelligent algorithm to figure out how the assignment should be done. If the assignment is between two variables of the same type, all is good. However, if the assignment is between different types, you could have serious problems.

For example, if you assigned an int value to a float variable it would convert okay because the fractional part of the new float would just be zero. However, if you went the other way and assigned a float value to an int variable, that would most likely be a problem. You would lose all of the precision of the original float value. Consider the damage that could be caused if the float value represented a chemical ingredient, an engineering measurement, or a financial value. Finding such an error would be difficult and particularly expensive, especially if the error didn't show up until your application was in production (already being used by customers).

Using the Cast Operator for Conversions

In Lesson 02: Operators, Types, and Variables, you learned about C# types and operators. It explained the size and precision of the various types and there is a list of available operators. The cast operator, (x), is listed first as a primary operator in Table 2-4. When you must convert a type that doesn't fit, it must be done via what is called an explicit conversion, which uses the cast operator. Listing 22-1 has an example of an implicit conversion, which doesn't require the cast operator, and an explicit conversion.

Listing 22-1. Cast Operators
using System;
class Program
{
    static void Main()
    {
        float lengthFloat = 7.35f;
        // lose precision - explicit conversion
        int lengthInt = (int)lengthFloat;
        // no problem - implicit conversion
        double lengthDouble = lengthInt;
        Console.WriteLine("lengthInt = " + lengthInt);
        Console.WriteLine("lengthDouble = " + lengthDouble);
        Console.ReadKey();
    }
}

Here's the output:

lengthInt = 7
lengthDouble = 7

Since a float, lengthFloat, has a fractional part but an int, lengthInt, doesn't; the types aren't compatible. Because of type safety, C# won't allow you to assign lengthFloat directly to lengthInt, which would be dangerous. For your protection, you must use a cast operator, (int), to force the explicit conversion of lengthFloat to lengthInt. In the output, you can see that lengthInt is 7, showing that it lost the fractional part of the 7.35f value from lengthFloat.

The assignment from lengthInt to lengthDouble is safe because a double is 64-bit and an int is 32-bit, meaning that you won't lose information. Therefore, the conversion is implicit, meaning that you can perform the assignment without the cast operator.

Understanding Reference Types

Reference type variables are named appropriately (reference) because the variable holds a reference to an object. In C and C++, you have something similar that is called a pointer, which points to an object. While you can modify a pointer, you can't modify the value of a reference - it simply points at the object in memory.

An important fact you need to understand is that when you are assigning one reference type variable to another, only the reference is copied, not the object. The variable holds the reference and that is what is being copied. Listing 22-2 shows how this works.

Listing 22-2. Reference Type Assignment
using System;
class Employee
{
    private string m_name;
    public string Name
    {
        get { return m_name; }
        set { m_name = value; }
    }
}
class Program
{
    static void Main()
    {
        Employee joe = new Employee();
        joe.Name = "Joe";
        Employee bob = new Employee();
        bob.Name = "Bob";
        Console.WriteLine("Original Employee Values:");
        Console.WriteLine("joe = " + joe.Name);
        Console.WriteLine("bob = " + bob.Name);
        // assign joe reference to bob variable
        bob = joe;
        Console.WriteLine();
        Console.WriteLine("Values After Reference Assignment:");
        Console.WriteLine("joe = " + joe.Name);
        Console.WriteLine("bob = " + bob.Name);
        joe.Name = "Bobbi Jo";
        Console.WriteLine();
        Console.WriteLine("Values After Changing One Instance:");
        Console.WriteLine("joe = " + joe.Name);
        Console.WriteLine("bob = " + bob.Name);
        Console.ReadKey();
    }
}

Here's the output:

Original Employee Values:
joe = Joe
bob = Bob
Values After Reference Assignment:
joe = Joe
bob = Joe
Values After Changing One Instance:
joe = Bobbi Jo
bob = Bobbi Jo

In Listing 22-2, I created two Employee instances, joe and bob. You can see in the output that the Name properties of both Employee instances each show their assigned values from when the objects were first created. After assigning joe to bob, the value of the Name properties of both instances are the same. This is what you might expect to see.

What might surprise you is the values that occur after assigning a value to the Employee instance variable named joe. If you look at the code closely, you'll notice that it doesn't change bob - only Joe. However, the results from the output show that the Name property in bob is the same as the Name property in joe. This demonstrates that after assigning joe to bob, both variables held references to the joe object. Only the reference was copied - not the object. This is why you see the results of printing Name in both joe and bob are the same because the change was on the object that they both refer to.

The following types are reference types:

  • arrays
  • class'
  • delegates
  • interfaces

Note: The primitive type, string, is also a reference type.

Understanding Value Types

Value type variables, as their name (value) suggests, hold the object value. A value type variable holds its own copy of an object and when you perform assignment from one value type variable to another, both the left-hand-side and right-hand-side of the assignment hold two separate copies of that value. Listing 22-3 shows how value type assignment works.

Listing 22-3. Value Type Assignment
using System;
struct Height
{
    private int m_inches;
    public int Inches
    {
        get { return m_inches; }
        set { m_inches = value; }
    }
}
class Program
{
    static void Main()
    {
        Height joe = new Height();
        joe.Inches = 71;
        Height bob = new Height();
        bob.Inches = 59;
        Console.WriteLine("Original Height Values:");
        Console.WriteLine("joe = " + joe.Inches);
        Console.WriteLine("bob = " + bob.Inches);
        // assign joe value to bob variable
        bob = joe;
        Console.WriteLine();
        Console.WriteLine("Values After Value Assignment:");
        Console.WriteLine("joe = " + joe.Inches);
        Console.WriteLine("bob = " + bob.Inches);
        joe.Inches = 65;
        Console.WriteLine();
        Console.WriteLine("Values After Changing One Instance:");
        Console.WriteLine("joe = " + joe.Inches);
        Console.WriteLine("bob = " + bob.Inches);
        Console.ReadKey();
    }
}

Here's the output:

Original Height Values: 
joe = 71 
bob = 59 
Values After Value Assignment: 
joe = 71 
bob = 71 
Values After Changing One Instance: 
joe = 65 
bob = 71

In Listing 22-3, you can see that the Inches property of bob and joe are initially set to different values. After assigning joe to bob, a value copy occurs, where both of the variables have the same value, but are two separate copies. To demonstrate value assignment results, notice what happens after setting joe to 65; The output shows that bob did not change, which demonstrates that value types hold distinct copies of their objects.

The following types are value types:

  • enum
  • struct

Note: All of the primitive types (int, char, double, etc.), except for string, are value types.

Reference Type and Value Type Differences

From the previous paragraphs, you might already see that there is a difference reference type and value type assignment. Reference types copy a reference to an object and value types copy the object. If you don't know this, then the effects can be surprising in your code when performing tasks such as making assignments and passing arguments to methods.

Summary

This lesson provided a few tips on working with types in C#. You should now have a better understanding of type safety and how it can help you avoid problems. This lesson showed you how to use a cast operator to perform conversions and explained the difference between explicit and implicit conversions. You also know that the type system is divided between reference types and value types. To demonstrate the differences between reference types and value types, this lesson provided examples that showed how both reference types and value types behave during assignment.

I invite you to return for Lesson 23: Working with Nullable Types.

Follow Joe Mayo on Twitter.

Feedback

Copyright © 2000 - 2014 C# Station, All Rights Reserved