Type Casting & Boxing/Unboxing
Real programs constantly move data between types — a user types "42" as text and you need the number; a calculation produces a double and you need a whole count. This lesson covers safe and unsafe conversions, the difference between Parse and TryParse , and the subtle world of boxing and unboxing that trips up many intermediate developers.
Learn Type Casting & Boxing/Unboxing in our free C# course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a quick…
Part of the free C# course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
Casting is like pouring liquid between containers . Pouring a small glass into a large jug ( int → double ) is always safe — it just fits. Pouring the big jug back into the small glass ( double → int ) might overflow, so C# makes you confirm with (int) that you accept spilling the extra. Boxing is putting that glass inside a sealed parcel (an object ) so it can travel through a system that only handles parcels; unboxing is opening the parcel — and you must know exactly what glass to expect.
1. Implicit vs Explicit Casting
When a conversion can never lose data — a smaller type going into a larger one — C# does it for you automatically ( implicit ). When data could be lost, you must spell out the target type in parentheses ( explicit ), which is your signature accepting the consequences. The classic gotcha: (int) on a decimal truncates (chops the fraction), it does not round.
2. Strings ↔ Numbers
Text and numbers are completely different types, so you can't cast between them with (int) — you convert. Going text → number uses Parse (throws on bad input) or TryParse (safe). Going number → text uses ToString , optionally with a format string like "C" or "F2" . For anything a user typed, always reach for TryParse .
Your turn. The two strings below came from a form. Convert each to the right number type, then run the calculation.
3. Boxing & Unboxing
Because every type in C# ultimately derives from object , a value type like int can be stored in an object variable. Doing so boxes it: the value is copied onto the heap inside a little wrapper. Reading it back out unboxes it, and you must cast to the exact original type — unboxing an int as a long throws at runtime even though the number would fit.
Boxing isn't free. Each box is a heap allocation , and the garbage collector later has to clean it up. In a tight loop that boxes millions of values, this becomes a real bottleneck — the classic example was the old non-generic ArrayList , which stored everything as object and boxed every int you added.
This is exactly why generics exist. stores int values directly with no boxing, so it's both faster and type-safe. Whenever you see code converting values to object in a loop, suspect accidental boxing and prefer a generic collection.
Here's the pattern you'll use constantly: take messy, possibly-invalid text and process only the valid parts without ever crashing.
These six lines safely convert text to a number and print a doubled result. Put them in the order that compiles and prints Doubled: 50 .
Why: text must be declared before TryParse reads it. The if declares n via out , so the WriteLine using n must follow it. The else must come immediately after the if body — an else with no preceding if is a syntax error.
7 — casting double to int truncates the fraction; it does not round to 8.
15 — 5 is boxed into box , then unboxed back to int as 5 , and 5 + 10 = 15 .
False 0 — "12abc" isn't a valid integer, so TryParse returns false and sets r to its default, 0 .
Combine everything: parse only valid text, track a running total and count, and divide carefully to avoid integer division. The outline is in the comments.
Practice quiz
An implicit cast in C# happens automatically when:
- The conversion can never lose data, e.g. int to double
- You write the (type) syntax explicitly
- Converting any number to a string
- Whenever two types share a base class
Answer: The conversion can never lose data, e.g. int to double. Implicit casts are allowed only when no data can be lost, such as a smaller type (int) widening into a larger one (double or long).
What does the explicit cast (int)3.99 evaluate to?
- 4
- 3
- 3.99
- A compile error
Answer: 3. Casting a double to an int truncates (drops the fractional part); it does not round. So (int)3.99 is 3.
What is (int)(-2.7) in C#?
- -3
- -2
- 2
- 0
Answer: -2. Truncation chops the fractional part toward zero, so -2.7 becomes -2, not -3.
Which method should you use to convert user-supplied text to a number without risking a crash?
- int.Parse
- int.TryParse
- (int) cast
- Convert.ToDouble
Answer: int.TryParse. TryParse returns false on bad input instead of throwing, so user input can be handled gracefully. Parse throws a FormatException.
What does int.Parse("12x") do at runtime?
- Returns 12
- Returns 0
- Throws a FormatException
- Returns null
Answer: Throws a FormatException. Parse throws a FormatException when the text is not a valid integer. TryParse would instead return false.
After bool ok = int.TryParse("abc", out int r); what are ok and r?
- true and 0
- false and 0
- false and -1
- true and abc
Answer: false and 0. TryParse fails on "abc", so it returns false and sets the out variable to its default, 0.
What is boxing in C#?
- Wrapping a value type inside an object on the heap
- Converting a string to a number
- Casting a double to an int
- Splitting an array into segments
Answer: Wrapping a value type inside an object on the heap. Boxing copies a value type (like int) into an object on the heap so it can be treated as a reference type.
Given object boxed = 5; which unboxing line is valid at runtime?
- long x = (long)boxed;
- double x = (double)boxed;
- int x = (int)boxed;
- short x = (short)boxed;
Answer: int x = (int)boxed;. A boxed value must be unboxed to its exact original type. The 5 was boxed as an int, so only (int)boxed succeeds; the others throw InvalidCastException.
Why is boxing best avoided inside a tight loop?
- It rounds numbers incorrectly
- Each box is a heap allocation that adds GC pressure
- It always throws an exception
- It converts ints to strings
Answer: Each box is a heap allocation that adds GC pressure. Every box is a separate heap allocation the garbage collector must later clean up, which is slow in hot paths. Generics like List<int> avoid it.
To round 3.99 to the nearest whole number instead of truncating, you should use:
- (int)3.99
- Math.Round(3.99)
- Math.Floor(3.99)
- int.Parse("3.99")
Answer: Math.Round(3.99). Math.Round rounds to the nearest whole number (giving 4 here). An (int) cast and Math.Floor would both truncate to 3.