Essential C# 2.0
Authors: Michaelis M.
Published year: 2007
Pages: 21-22/185
Buy this book on amazon.com >>

Nullable Modifier

As I pointed out earlier, value types cannot be assigned null because, by definition, they can't contain references, including references to nothing. However, this presents a problem in the real world, where values are missing. When specifying a count, for example, what do you enter if the count is unknown? One possible solution is to designate a "magic" value, such as or int.Max , but these are valid integers. Rather, it is desirable to assign null to the value type because this is not a valid integer.

To declare variables that can store null you use the nullable modifier, ? . This C# 2.0 feature appears in Listing 2.17.

Listing 2.17. Using the Nullable Modifier

static void

Main()
{


int?


count =

null;


do

{
      // ...
  }

while

(count ==

null

);
}

Assigning null to value types is especially attractive in database programming. Frequently value type columns in database tables allow null s. Retrieving such columns and assigning them to corresponding fields within C# code is problematic , unless the fields can contain null as well. Fortunately, the nullable modifier is designed to handle such a scenario specifically .



Conversions between Data Types

Given the thousands of types predefined in the various CLI implementations and the unlimited number of types that code can define, it is important that types support conversion from one to another where it makes sense. The most common type of conversion is casting .

Consider the conversion between two numerical types: converting from a variable of type long to a variable of type int . A long type can contain values as large as 9,223,372,036,854,775,808; however, the maximum size of an int is 2,147,483,647. As such, that conversion could result in a loss of datafor example, if the variable of type long contains a value greater than the maximum size of an int . Any conversion that could result in a loss of data or an exception because the conversion failed requires an explicit cast . Conversely, a casting operation that will not lose precision and will not throw an exception regardless of the operand types is an implicit cast .

Explicit Cast

In C#, you cast using the cast operator . By specifying the type you would like the variable converted to within parentheses, you acknowledge that if an explicit cast is occurring, there may be a loss of precision and data, or an exception may result. The code in Listing 2.18 converts a long to an int and explicitly tells the system to attempt the operation.

Listing 2.18. Explicit Cast Example

With the cast operator, the programmer essentially says to the compiler, "Trust me, I know what I am doing. I know that the conversion could possibly not fit but I am willing to take the chance." Making such a choice will cause the compiler to allow the conversion. However, with an explicit conversion, there is still a chance that an error, in the form of an exception, might occur at runtime if the data does not convert successfully. It is, therefore, the programmer's responsibility to ensure the data will successfully convert, or else to provide the necessary code logic when it doesn't.

Advanced Topic: Checked and Unchecked Conversions

C# provides special keywords for marking a code block to indicate what should happen if the target data type is too small to contain the assigned data. By default, if the target data type cannot contain the assigned data, then the data will truncate during assignment. For an example, see Listing 2.19.

Listing 2.19. Overflowing an Integer Value

public


class

Program
{

public


static


void

Main()
  {

// int.MaxValue equals 2147483647


int

n =

int

.MaxValue;
       n = n + 1 ;
       System.Console.WriteLine(n);
  }
}

Output 2.14 shows the results.

Output 2.14.
-2147483648

Listing 2.19 writes the value -2147483648 to the console. However, placing the code within a checked block , or using the checked option when running the compiler, will cause the runtime to throw an exception of type System.OverflowException . The syntax for a checked block uses the checked keyword, as shown in Listing 2.20.

Listing 2.20. A Checked Block Example

public


class

Program
{

public


static


void

Main()
  {


checked



{


// int.MaxValue equals 2147483647


int

n =

int

.MaxValue;
          n = n + 1 ;
          System.Console.WriteLine(n);

}

}
}

Output 2.15 shows the results.

Output 2.15.
Unhandled Exception: System.OverflowException: Arithmetic operation
resulted in an overflow at Program.Main() in ...Program.cs:line 12

The result is that an exception is thrown if, within the checked block, an overflow assignment occurs at runtime.

The C# compiler provides a command-line option for changing the default checked behavior from unchecked to checked. C# also supports an unchecked block that truncates the data instead of throwing an exception for assignments within the block (see Listing 2.21).

Listing 2.21. An Unchecked Block Example

using

System;

public


class

Program
{

public


static


void

Main()
  {


unchecked



{


// int.MaxValue equals 2147483647


int

n =

int

.MaxValue;
           n = n + 1 ;
           System.Console.WriteLine(n);

}

}
}

Output 2.16 shows the results.

Output 2.16.
-2147483648

Even if the checked option is on during compilation, the unchecked keyword in the preceding code will prevent the runtime from throwing an exception during execution.


You cannot convert any type to any other type simply because you designate the conversion explicitly using the cast operator. The compiler will still check that the operation is valid. For example, you cannot convert a long to a bool . No such cast operator is defined, and therefore, the compiler does not allow such a cast.

Language Contrast: Converting Numbers to Booleans

It may be surprising that there is no valid cast from a numeric type to a Boolean type, since this is common in many other languages. The reason no such conversion exists in C# is to avoid any ambiguity, such as whether 1 corresponds to true or false. More importantly, as you will see in the next chapter, this also reduces the chance of using the assignment operator in place of the equality operator (avoiding if(x=42){...} when if(x==42){...} was intended, for example).


Implicit Cast

In other instances, such as going from an int type to a long type, there is no loss of precision and there will be no fundamental change in the value of the type. In these cases, code needs only to specify the assignment operator and the conversion is implicit . In other words, the compiler is able to determine that such a conversion will work correctly. The code in Listing 2.22 converts from an int to a long by simply using the assignment operator.

Listing 2.22. Not Using the Cast Operator for an Implicit Cast

int

intNumber = 31416;

long

longNumber = intNumber;

Even when no explicit cast operator is required (because an implicit conversion is allowed), it is still possible to include the cast operator (see Listing 2.23).

Listing 2.23. Using the Cast Operator for an Implicit Cast

int

intNumber = 31416;

long

longNumber = (

long

) intNumber;

Type Conversion without Casting

Neither an implicit nor an explicit cast is defined from a string to a numeric type, so methods such as Parse() are required. Each numeric data type includes a Parse() function that enables conversion from a string to the corresponding numeric type. Listing 2.24 demonstrates this call.

Listing 2.24. Using int.Parse() to Convert a string to a Numeric Data Type

string

text = "9.11E-31";

float

kgElectronMass =

float

.Parse(text);

Another special type is available for converting one type to the next. The type is System.Convert and an example of its use appears in Listing 2.25.

Listing 2.25. Type Conversion Using System.Convert

string

middleCText = "278.4375";

double

middleC = System.Convert.ToDouble(middleCText);

bool

boolean = System.Convert.ToBoolean(middleC);

System.Convert supports only a predefined number of types and it is not extensible.

Furthermore, all types support a ToString() method that can be used to provide a string representation of a type. Listing 2.26 demonstrates how to use this method. The resulting output is shown in Output 2.17.

Listing 2.26. Using ToString() to Convert to a string

bool

boolean =

true

;

string

text = boolean.ToString();

// Display "True"

System.Console.WriteLine(text);

Output 2.17.
True

For the majority of types, the ToString() method will return the name of the data type rather than a string representation of the data. The string representation is returned only if the type has an explicit implementation of ToString() . One last point to make is that it is possible to code custom conversion methods, and many such methods are available for classes in the runtime.

Advanced Topic: tryParse()

In C# 2.0, all the numeric primitive types include a static tryParse() method. (In C# 1.0, only double includes such a method.) This method is very similar to the Parse() method, except that instead of throwing an exception if the conversion fails, the tryParse() method returns false , as demonstrated in Listing 2.27.

Listing 2.27. Using TRyParse() in Place of an Invalid Cast Exception

double

number;

string

input;

System.Console.Write("Enter a number: ");
input = System.Console.ReadLine();


if

(

double

.TryParse(input,

out

number))


{



// Converted correctly, now use number




// ...



}


else

{
   System.Console.WriteLine(
       "The text entered was not a valid number.");
}

Output 2.18 shows the results of Listing 2.27.

Output 2.18.
Enter a number:

forty-two

The text entered was not a valid number.

The resulting value the code parses from the input string is returned via an out parameterin this case, number .

The key difference between Parse() and tryParse() is the fact that TRyParse() won't throw an exception if it fails. Frequently, the conversion from a string to a numeric type depends on a user entering the text. It is expected, in such scenarios, that the user will enter invalid data that will not parse successfully. By using TRyParse() rather than Parse() , you can avoid throwing exceptions in expected situations. (The expected situation in this case is that the user will enter invalid data.)



Essential C# 2.0
Authors: Michaelis M.
Published year: 2007
Pages: 21-22/185
Buy this book on amazon.com >>