LearnAI ToolsCareerPractice BuildsPlayContact
Lesson 1015 min read

Type Conversion

Learn implicit and explicit type conversion, casting, and the Convert and Parse methods.

Implicit Conversion

C# automatically converts between compatible types when no data would be lost — for example, converting an `int` to a `double`, since every whole number is also perfectly representable as a floating-point number. This is called a "widening" conversion, because you're moving to a type that can hold everything the original could, and more.

int wholeNumber = 42;
double decimalNumber = wholeNumber; // implicit, safe — no data can be lost
Console.WriteLine(decimalNumber);
long bigNumber = wholeNumber; // int → long is also implicit
float f = wholeNumber; // int → float is also implicit
Output

Click Run to see what this code prints.

FromToAlways Implicit?
intlong, float, double, decimalYes
floatdoubleYes
charintYes (a char's underlying numeric code)
longintNo — requires an explicit cast, covered next

Explicit Conversion (Casting)

Converting from a larger/more-precise type to a smaller/less-precise one risks losing data, so C# requires an explicit cast — parentheses around the target type — to make that risk visible directly in your code, rather than letting it happen silently.

double price = 19.99;
int roundedDown = (int)price; // explicit cast — truncates, doesn't round!
Console.WriteLine(roundedDown);
long bigNumber = 5_000_000_000;
int truncated = (int)bigNumber; // may lose data if it doesn't fit in an int
Output

Click Run to see what this code prints.

Casting a double to int Truncates, It Doesn't Round

(int)19.99 produces 19, not 20 — casting simply chops off everything after the decimal point. If you actually want rounding, use Math.Round() first, then cast the result: (int)Math.Round(19.99).

What Happens When Data Is Lost

When a value genuinely doesn't fit in the smaller target type, the result is not an error by default — it silently wraps around to an unexpected value, which can be a genuinely dangerous source of subtle bugs in production code.

long tooLarge = 5_000_000_000; // larger than int can hold
int result = (int)tooLarge;
Console.WriteLine(result); // 705032704 — NOT 5 billion, and NOT an error!

The Convert Class

The `Convert` class handles conversions between otherwise unrelated types that a simple cast can't bridge, like turning a string into a number — casting only works between numerically related types, not between a string and an int.

string input = "25";
int age = Convert.ToInt32(input);
Console.WriteLine(age + 5);
// Convert also handles bool, DateTime, and more
bool flag = Convert.ToBoolean("true");
string backToString = Convert.ToString(42);
Output

Click Run to see what this code prints.

Convert.ToInt32 Still Throws on Invalid Input

Convert.ToInt32("not a number") throws a FormatException — Convert is more flexible than a plain cast, but it is not automatically "safe" for untrusted input; that's exactly what Parse/TryParse (covered next) are for.

Parse & TryParse

`Parse` (called on the target type itself, like int.Parse) throws an exception if the string isn't a validly formatted value of that type. `TryParse` is the safer alternative — it returns `true`/`false` to indicate success instead of throwing, storing the actual result in an `out` parameter. This distinction matters enormously when handling real, untrustworthy user input.

string userInput = "abc";
if (int.TryParse(userInput, out int result))
{
Console.WriteLine($"Parsed: {result}");
}
else
{
Console.WriteLine("Invalid number.");
}
Output

Click Run to see what this code prints.

Prefer TryParse for User Input

Any time you're converting input that might not be a valid number (like form data, console input, or an external API response), use TryParse instead of Parse to avoid crashing your program on unexpected input — this is one of the single most impactful habits for writing robust, production-quality C# code.

A full comparison of when to reach for each conversion mechanism:

MechanismFrom → ToOn Invalid Input
Implicit conversionA compatible, wider typeNot applicable — the compiler guarantees safety
Explicit cast (T)A related, narrower numeric typeSilently produces wrong data (no exception)
Convert.ToX()Unrelated types (string number, etc.)Throws FormatException
X.Parse()string → a specific typeThrows FormatException
X.TryParse()string → a specific typeReturns false, no exception

Boxing and Unboxing

Boxing is the process of wrapping a value type (like an int) inside an object reference, so it can be treated as a reference type — necessary when passing a value type somewhere that expects the general-purpose object type. Unboxing reverses this, extracting the original value type back out. Both involve real, measurable performance overhead compared to working with the value type directly, since boxing allocates new memory on the heap.

int number = 42;
object boxed = number; // boxing — number is wrapped in an object on the heap
int unboxed = (int)boxed; // unboxing — explicit cast required
Console.WriteLine(unboxed); // 42
Fun Fact

Generics (covered in a dedicated lesson later in this course) were introduced in C# 2.0 largely to eliminate exactly this kind of unnecessary boxing — older, pre-generics collection types stored everything as object, forcing a box/unbox round trip for every value type stored or retrieved.

Checked and Unchecked Contexts

By default, C# arithmetic overflow (like adding 1 to int.MaxValue) wraps around silently rather than throwing — an "unchecked" context, which is the implicit default for performance reasons. Wrapping code in an explicit checked block makes C# throw an OverflowException instead, trading a small amount of performance for safety.

int max = int.MaxValue;
int wrapped = max + 1; // unchecked (default) — silently wraps to a very negative number
Console.WriteLine(wrapped); // -2147483648
checked
{
int willThrow = max + 1; // throws System.OverflowException
}

Common Beginner Mistakes

Assuming a cast rounds a double to the nearest int

(int)19.99 truncates to 19, it does not round to 20 — use Math.Round() explicitly first if you want mathematical rounding.

Using Parse on untrusted input instead of TryParse

A single unexpected character in a user-typed string crashes int.Parse() with an unhandled exception — TryParse avoids this entirely with a simple boolean check.

Not realizing a narrowing cast can silently corrupt data

Casting a long that's too large for int doesn't throw an error by default — it produces a wrong, wrapped-around value that can be very hard to trace back to its source.

FAQs

It overflows silently in the default unchecked context, producing an unexpected wrapped value — always validate ranges before casting untrusted or externally-sourced data, or wrap the cast in a checked block.

Yes — double.TryParse, bool.TryParse, DateTime.TryParse, decimal.TryParse, and more all follow the identical pattern.

Because sometimes truncation or wrapping is genuinely the intended behavior (like deliberately taking only the whole-number part of a price) — the cast syntax simply makes clear, at the point you write the code, that you are aware data could be affected.

Rarely with modern C# and generics — it mostly matters in performance-critical code, or when working with older non-generic APIs (like the ArrayList class) that predate generics.

Key Takeaways

  • Implicit conversion is automatic and safe (no data loss) — only allowed when the compiler can guarantee correctness.
  • Explicit casting is required when data might be lost, and truncates (doesn't round) when converting floating-point to integer.
  • Convert.ToX() bridges unrelated types like string and int, but still throws on invalid input.
  • Prefer TryParse over Parse whenever converting untrusted or user-provided input.
  • By default, arithmetic overflow wraps silently (unchecked); wrap in a checked block to throw an exception instead.

Summary

Type conversion in C# ranges from fully automatic and safe (implicit) to explicit and risky (casting) to defensively safe (TryParse) — choosing the right mechanism for the right situation is a hallmark of careful, production-quality code. Next, you'll learn the operators C# uses to actually compute and compare these values.

Next Lesson →

Operators