So far in this series we have discussed the robust type system in C# and what kinds of primitive types can exist.
Sometimes we want to take an object and change its type; for example, take a value that was an int
and change it to a double
, or take a float
and turn it into a long
. We can do this in two ways: casting and conversion.
We can also take objects of type string
and attempt to change their value into a different type through parsing.
The Sample Project
Casting
Casting is taking an object and attempting to "force" it to change types. When a cast is attempted, if the value of the object is allowable in the new type, the object will be casted into an object of the specified type.
We cast a value by placing the targeted type in parentheses ()
next to the value we want to cast.
C#'s compiler allows many different kinds of casting. For example, we can cast an int
to a double
, a char
to an int
, or a float
to a decimal
.
int five = 5;
var doubleFive = (double)five;
char a = 'a';
var valueA = (int)a;
float myFloat = 4.56F;
decimal myMoney = (decimal)myFloat;
For each of these casts (and many others) the C# compiler will "force" the value into a new variable of the specified type. This works as long as the range of the new type includes the value. However, some casts will fail because the types are not compatible, such as:
There is no way to determine if the value of myString
can be converted to a byte
, so the C# compiler will throw an error. In fact, the normal way to convert a string
to any other type is through parsing, which is discussed later in this post.
Further, casting from a more-precise type to a less-precise type will result in a loss of precision:
decimal myMoney = 5.87M;
int intMoney = (int)myMoney; //Value is now 5; the .87 was lost
Because of this, we need to be careful when converting more-precise types (e.g. the floating-point numeric types) to less-precise types (int
, long
, char
, etc).
Conversion
A conversion is similar to a cast in that it takes a value of a particular type and changes it into a value of another type. However, conversions are more forgiving than casts, generally do not lose precision, and take computationally longer to execute.
The .NET Framework provides us with a class called Convert
. This class can take values from all the primitive types and attempt to convert them to all other primitive types.
int five = 5;
decimal decFive = Convert.ToDecimal(five);
decimal myMoney = 5.67M;
int intMoney = Convert.ToInt32(myMoney); //Value is now 6;
//the decimal value was rounded
When casting a floating-point numeric type to an integral numeric type, the numbers after the decimal point are lost. When converting, the value is instead rounded to the nearest whole number using a methodology known as "banker's rounding": if the number is exactly halfway between two whole numbers the even number is returned (e.g. if the number is 4.5, return 4; if the number is 5.5, return 6); otherwise, round to the nearest whole number.
The Convert
class can also handle numeric to non-numeric and vice-versa conversions, such as:
string five = "5.0";
decimal decFive = Convert.ToDecimal(five); //Value is 5.0
double myValue = 5.33;
string stringValue = Convert.ToString(myValue); //Value is "5.33"
int intTrue = 1;
bool isTrue = Convert.ToBoolean(intTrue); //Value is true because number is not 0
Generally speaking, casting is faster but more prone to errors, and conversion is slower but more likely to succeed. Which one you use is a decision you have to make.
Parse()
As mentioned earlier, the string
type has a unique place among the C# primitive types. Because it is a reference type, it needs special handling when converting from it to other types. We call this parsing.
The .NET Framework provides us with Parse()
and TryParse()
methods on each primitive type to handle converting from a string
to that type.
If we needed to parse a string
to a decimal
, we could use the Parse()
method:
string decString = "5.632";
decimal decValue = decimal.Parse(decString); //Value is 5.632M
However, if the string
cannot be parsed to an acceptable value for the target type, the Parse()
method will throw an exception:
string testString = "10.22.2000";
double decValue = double.Parse(testString); //Exception thrown here!
string intTest = "This is a test string";
int intValue = int.Parse(intTest); //Exception thrown here!
TryParse()
For situations where we don't know if the string
value can be parsed to the desired type, we can use the method TryParse()
:
string value = "5.0";
decimal result;
bool isValid = decimal.TryParse(value, out result);
If isValid
is true, then the string value was successfully parsed and is now the value of the variable result
.
The usage of the out
keyword is explored in Part 6 (Methods, Parameters, and Arguments) of this series:
Is Keyword
There are occasions in which we do not know the specific type of a given object. Very often this happens if the code retrieved the object from another source, such as an external database, API, or service. For this situation, C# provides us with the is
keyword which tests if an object is of a particular type:
var myValue = 6.5M; //M literal means type will be decimal
if(myValue is decimal) { /*...*/ }
The is
keyword returns true
if the object is of the specified type, and false
otherwise.
As Keyword
For reference types, C# provides us with the as
keyword to convert one reference type to another.
string testString = "This is a test"; //string is a reference type
object objString = (object)testString; //Cast the string to an object
string test2 = objString as string; //Will convert to string successfully
Note that this only works on valid conversions; types which do not have a defined conversion will throw an exception:
public class ClassA { /*...*/ }
public class ClassB { /*...*/ }
var myClass = new ClassA();
var newClass = myClass as ClassB; //Exception thrown here!
However, classes which inherit from one another can be freely converted using as
:
We discuss inheritance more thoroughly in Part 9 (Inheritance and Polymorphism). If you want, you can skip ahead to that part:
GetType() and Typeof
For any given object in C#, we can get its type as an object by calling the GetType()
method:
var sentence = "This is a sentence.";
var type = sentence.GetType();
We can then check if the given type is a known type, such as a primitive, a class, or others by using the typeof
keyword.
var sentence = "This is a sentence.";
var type = sentence.GetType();
if(type == typeof(string)) { /*...*/ }
else if (type == typeof(int)) { /*...*/ }
Glossary
- Casting - "Forcing" a value from one type to another type; prone to errors.
- Conversion - Attempting to change one object's type to another; more forgiving but computationally more expensive.
- Parsing - Attempting to convert a string to a different primitive type.
New Keywords
is
- Used to check if a particular value is of a given type.as
- Used to convert an object from one type to another.typeof
- Returns the type of a given object.
Summary
Casting and converting are ways in which we can change a value from one type to another; casting is faster but more prone to errors, while conversion is more computationally expensive but also more forgiving.
Parsing is a special form of conversion that deals with getting a value from an object of type string
and changing that value to another type.
Using the keywords is
and as
, we can check for type equality between two objects and determine if one object can be changed to a different type respectively.
Finally, the special method GetType()
and the typeof
keyword can be used to check if objects are of a particular type.
Got questions about casting, conversion, parsing, and the like? Ask questions in the comments below! The community and I are happy to help.
In the next part of this series, we turn our attention to some of the most basic C# features: operators. Want to learn how to do math, assignment, equality checking, and more in C#? Check this post out!
Happy Coding!