Type Conversion
Integer arithmetic is unchecked by default: overflowing wraps around silently (int.MaxValue + 1 becomes int.MinValue).
Concept
Implicit and Explicit Conversions Implicit conversions happen automatically when no data can be lost: int → long, int → double, float → double, derived class → base class.
Explicit conversions (casts) are required when data may be lost: double → int (truncates toward zero), long → int, base class → derived class. Syntax: (int)value.
Numeric Conversion and Overflow Integer arithmetic is unchecked by default: overflowing wraps around silently (int.MaxValue + 1 becomes int.MinValue). Use checked(...), a checked { } block or the <CheckForOverflowUnderflow> project setting to throw OverflowException instead. A *constant* that does not fit is a compile-time error.
Convert, Parse and TryParse Convert.ToInt32(x) etc.
Converts between base types; rounds floating values to the nearest integer, with ties to even (banker's rounding); handles null as 0
Parses text; throws FormatException (bad text) or OverflowException int.TryParse(s, out int v)
Returns true/false, never throws — preferred for user input double.Parse(s, culture)
Parsing is culture-sensitive: "3,75" means 3.75 in German Boxing and Unboxing
Boxing wraps a value type in an object on the heap when it is converted to object or an interface. Unboxing extracts it and must use the exact original type, otherwise InvalidCastException. Boxing allocates, so avoid it in hot loops (generics prevent most boxing).
Types can declare implicit operator (safe, lossless) and explicit operator (may fail or lose information) conversions.
Why It Matters
Input from users, files, sensors and networks arrives as text or raw numbers; converting it safely — with the right culture and without silent overflow — is everyday work and a major source of bugs.
Syntax
double d = someInt; // implicit
int i = (int)someDouble; // explicit cast
int n = checked(a + b); // throw on overflow
int.TryParse(text, out int value); // safe parse
public static implicit operator Target(Source s) => ...;Example
using System.Globalization;
// Implicit (widening, never loses data) and explicit (narrowing) conversions
int number = 100;
double widened = number; // implicit
double reading = 97.86;
int truncated = (int)reading; // explicit cast truncates toward zero
Console.WriteLine($"implicit: {widened}, explicit (int)97.86 = {truncated}, (int)-2.7 = {(int)-2.7}");
// Overflow: unchecked by default, checked on request
int big = int.MaxValue;
int wrapped = unchecked(big + 1);
Console.WriteLine($"unchecked int.MaxValue + 1 = {wrapped}");
try
{
int boom = checked(big + 1);
Console.WriteLine(boom);
}
catch (OverflowException ex)
{
Console.WriteLine($"checked: {ex.GetType().Name}: {ex.Message}");
}
int threeHundred = 300;
Console.WriteLine($"(byte)threeHundred = {(byte)threeHundred} (300 mod 256)");
// Convert rounds (to even!), casts truncate
Console.WriteLine($"Convert.ToInt32(2.5) = {Convert.ToInt32(2.5)}, Convert.ToInt32(3.5) = {Convert.ToInt32(3.5)}, Math.Round(2.5, MidpointRounding.AwayFromZero) = {Math.Round(2.5, MidpointRounding.AwayFromZero)}");
Console.WriteLine($"Convert.ToBoolean(\"true\") = {Convert.ToBoolean("true")}, Convert.ToString(255, 1
…Expected Result
Note Convert.ToInt32(2.5) is 2 (ties go to the even number), while a cast would truncate and Math.Round(..., AwayFromZero) gives 3.
implicit: 100, explicit (int)97.86 = 97, (int)-2.7 = -2
unchecked int.MaxValue + 1 = -2147483648
checked: OverflowException: Arithmetic operation resulted in an overflow.
(byte)threeHundred = 44 (300 mod 256)
Convert.ToInt32(2.5) = 2, Convert.ToInt32(3.5) = 4, Math.Round(2.5, MidpointRounding.AwayFromZero) = 3
Convert.ToBoolean("true") = True, Convert.ToString(255, 16) = ff
int.Parse("123") + 1 = 124
TryParse("42") -> 42
TryParse("4x2") -> failed
TryParse("") -> failed
Parse("4x2") -> FormatException: The input string '4x2' was not in a correct format.
de-DE "3,75" = 3.75, invariant "3.75" = 3.75
boxed is Int32, unboxed = 88
(long)boxedInt -> InvalidCastException
100 C = 212 F = 100 CCommon Mistakes
Assigning a double to an int without a cast — real compiler error:
Casting a constant that does not fit:
Using Parse on user input and crashing on bad text — use TryParse.
Parsing numbers with the current culture when the data uses . — pass CultureInfo.InvariantCulture.
Expecting Convert.ToInt32(2.5) to be 3.
Unboxing to a different type ((long)boxedInt).
Relying on silent integer overflow.
double voltage = 12.6;
int whole = voltage; // narrowing conversion needs a cast
Console.WriteLine(whole);m06_err.cs(2,13): error CS0266: Cannot implicitly convert type 'double' to 'int'. An explicit conversion exists (are you missing a cast?)Console.WriteLine((byte)300);m06_err2.cs(1,19): error CS0221: Constant value '300' cannot be converted to a 'byte' (use 'unchecked' syntax to override)Best Practices
Use TryParse for anything a human or external system produces.
Store and exchange data in invariant formats (ISO dates, . decimal separator); format for humans only at the UI.
Enable checked arithmetic where overflow indicates a bug (counters, sizes).
Provide implicit conversions only when they never lose information and never throw.
Interview Relevance
High: "implicit vs explicit conversion", "Parse vs TryParse vs Convert", "what is boxing and why is it expensive?", "what does checked do?".
Practice Questions
Read a number from a string array and add them up, skipping invalid entries.
Convert 1234.5678 to int using a cast, Convert, and Math.Round. Compare.
Parse "1.234,56" as a German number.
Show that boxing creates a copy: box an int, change the original, print both.
Add an explicit conversion from Kilometers to Miles in a record struct.
Quick Quiz
Q1. (Predict the output) Console.WriteLine((int)-2.7);
Answer: -2 — casts truncate toward zero (verified).
Q2. (Predict the output) Console.WriteLine(Convert.ToInt32(2.5));
Answer: 2 — banker's rounding to the even integer (verified).
Q3. (True/False) int.TryParse throws FormatException for invalid text.
Answer: False. It returns false; int.Parse throws.
Q4. (Find the bug) object o = 5; long l = (long)o; Answer: Unboxing must use the exact type: (long)(int)o. The code throws InvalidCastException (verified).
Coding Challenge
Write a method bool TryReadVoltage(string text, out double volts) that accepts both "12.6" and "12,6", rejects values outside 0–60 V, and never throws.
using System.Globalization;
static bool TryReadVoltage(string text, out double volts)
{
string normalized = text.Trim().Replace(',', '.');
return double.TryParse(normalized, NumberStyles.Float, CultureInfo.InvariantCulture, out volts)
&& volts is >= 0 and <= 60;
}