Type Conversions in C#
last modified June 3, 2025
Type conversion in C# refers to the process of converting a value from one data type to another. This tutorial explores implicit and explicit conversions, boxing/unboxing, overflow handling, string conversions, and type promotion in expressions, providing practical examples to illustrate each concept.
C# supports two main types of conversions: implicit conversions (automatic and safe) and explicit conversions (requiring a cast, with potential data loss). Type promotion, a subset of implicit conversions, occurs in expressions to ensure compatibility between types. Understanding these mechanisms is crucial for writing robust C# code and avoiding common pitfalls like overflow or precision loss.
Type Conversion Comparison Table
The following table summarizes key type conversion features in C#:
Feature | C# Behavior |
---|---|
Implicit Conversions | Automatic, no data loss (e.g., int → long, long → double) |
Explicit Conversions | Requires cast, may lose data (e.g., double → int) |
Boxing | Converts value type to object or interface (implicit) |
Unboxing | Extracts value type from object (explicit, may throw InvalidCastException) |
Overflow Control | Checked/unchecked contexts for arithmetic operations |
String Conversion | Convert, Parse, TryParse for parsing strings to other types |
Type Promotion | Automatic promotion in expressions to wider type (e.g., int + double → double) |
This table provides a quick overview of C#'s type conversion features, highlighting the differences between implicit and explicit conversions, boxing/unboxing, overflow control, string conversion methods, and type promotion in expressions. Understanding these features is essential for effective C# programming.
Numeric Type Promotion Hierarchy
The following table outlines C#'s numeric type promotion rules for expressions:
Operation | C# Result Type | Notes |
---|---|---|
byte + byte | int | byte promoted to int |
short + short | int | short promoted to int |
char + char | int | char promoted to int |
int + long | long | int promoted to long |
long + float | float | long promoted to float |
float + double | double | float promoted to double |
C# promotes smaller types to int
in expressions involving
byte
, short
, or char
. For mixed-type
operations, the result is promoted to the wider type to prevent data loss.
Promotion Hierarchy Demonstration
This example demonstrates the promotion hierarchy in C# by performing various arithmetic operations between different numeric types. The result of each operation is stored in an object array, and the type and value of each result are printed to show how C# promotes types in mixed expressions.
byte b = 10; short s = 20; char c = 'A'; int i = 30; long l = 100L; float f = 5.5f; double d = 2.2; // Demonstrating promotion hierarchy object[] results = [ b + b, // byte + byte → int s + s, // short + short → int c + c, // char + char → int i + i, // int + int → int l + l, // long + long → long f + f, // float + float → float d + d, // double + double → double b + s, // byte + short → int s + c, // short + char → int i + l, // int + long → long l + f, // long + float → float f + d // float + double → double ]; foreach (var r in results) { Console.WriteLine(r.GetType() + ": " + r); }
This code shows how C# automatically promotes operands to the appropriate type in arithmetic expressions. The output displays the resulting type and value for each operation, illustrating the promotion rules in practice.
Contexts Where Type Conversion Occurs
Type conversions in C# occur in various contexts, as summarized below:
Context | C# Behavior | Example |
---|---|---|
Assignment | Implicit for widening, explicit for narrowing | int i = 100; long l = i; |
Method Arguments | Implicit promotion to match parameter types | void Print(long x); called with Print(5); |
Arithmetic Operations | Promotes to widest type involved | byte + short → int |
Array Initialization | Implicit conversion to array's type | double[] arr = {1, 2, 3.5}; |
Conditional Operator | Promotes to common type | (true) ? 5 : 5.5 → double |
Compound Assignment | Promotes right-hand side, casts back to left-hand type | byte b = 1; b += 2; |
Return Statements | Promotes to method's return type | return 5; in a double method |
These contexts illustrate how C# handles type conversions automatically or explicitly, depending on the situation. Understanding these contexts helps developers write code that avoids common pitfalls like overflow or precision loss.
Type Conversion in Array Initialization
C# automatically performs type conversion when initializing arrays, promoting or converting elements to match the declared array type. This allows you to mix compatible types in the initializer, as long as each value can be implicitly converted to the array's element type. If a value cannot be implicitly converted, an explicit cast is required.
int[] intArr = [1, 2, 3]; double[] doubleArr = [1, 2, 3.5]; object[] objArr = [1, "hello", 3.14]; Console.WriteLine(string.Join(", ", intArr)); Console.WriteLine(string.Join(", ", doubleArr)); Console.WriteLine(string.Join(", ", objArr));
In this example, the double[]
array is initialized with both int
and double values; the int values are automatically promoted to double. The
object[]
array can hold any type, so all elements are converted to
object
.
Implicit Conversions
Implicit conversions occur automatically when a value of a smaller type is assigned to a variable of a larger type, ensuring no data loss. These conversions follow C#'s type hierarchy and are safe.
byte b = 100; short s = b; // byte → short int i = s; // short → int long l = i; // int → long float f = l; // long → float double d = f; // float → double Console.WriteLine($@"byte: {b}, short: {s}, int: {i} long: {l}, float: {f}, double:{d}");
This example demonstrates the implicit conversion hierarchy in C#:
byte → short → int → long → float → double
. Each assignment is
performed automatically because the target type can fully represent the source
value without loss. For example, a byte (8 bits) fits safely into a short (16
bits), and so on.
Explicit Conversions (Casting)
Explicit conversions, or casting, are required when converting from a larger type to a smaller type, which may result in data loss or truncation.
double d = 123.456; int i = (int)d; // Explicit cast: truncates decimal part byte b = (byte)i; // Explicit cast: may lose data if i > 255 Console.WriteLine($"double: {d}, int: {i}, byte: {b}");
In this example, casting double
to int
truncates the
decimal part, resulting in 123
. Casting int
to
byte
risks data loss if the integer exceeds the byte's range
(0-255).
Boxing and Unboxing
Boxing converts a value type to a reference type (e.g.,
object
or an interface), while unboxing extracts the
value type from the reference type. Unboxing requires an explicit cast and can
throw an InvalidCastException
if the types mismatch.
int num = 42; object boxed = num; // Boxing: int to object int unboxed = (int)boxed; // Unboxing: object to int Console.WriteLine($"Boxed type: {boxed.GetType().Name}, Value: {boxed}"); Console.WriteLine($"Unboxed value: {unboxed}"); try { object str = "123"; int wrong = (int)str; // Throws InvalidCastException } catch (InvalidCastException e) { Console.WriteLine($"Error: {e.Message}"); }
Boxing wraps the integer 42
into an object
, and
unboxing retrieves it. This example shows that unboxing a string as an
integer throws an InvalidCastException
. The output includes the
boxed type and value, the unboxed value, and the error message.
Checked and Unchecked Contexts
C# provides checked
and unchecked
contexts to control
arithmetic overflow behavior. In checked
mode, overflows throw an
OverflowException
. In unchecked
mode (default),
overflows wrap around.
try { checked { int max = int.MaxValue; int result = max + 1; // Throws OverflowException Console.WriteLine(result); } } catch (OverflowException e) { Console.WriteLine($"Checked overflow: {e.Message}"); } unchecked { int max = int.MaxValue; int result = max + 1; // Wraps around Console.WriteLine($"Unchecked result: {result}"); }
In the checked
block, adding 1 to int.MaxValue
throws
an OverflowException
. In the unchecked
block, the
result wraps to int.MinValue
. This feature allows developers to
enforce strict overflow checking in critical calculations. The output shows the
exception message and the wrapped result.
Type Conversion in Method Arguments
C# automatically promotes or converts arguments to match the parameter types of a method when possible. This allows you to pass values of smaller or compatible types to methods expecting a wider type, without requiring explicit casts. However, narrowing conversions (from a wider to a smaller type) require an explicit cast and may result in data loss.
void PrintVal(long value) { Console.WriteLine($"Long value: {value}"); } int i = 42; byte b = 10; PrintVal(i); // int promoted to long PrintVal(b); // byte promoted to long double d = 123.45; // PrintVal(d); // Error: cannot implicitly convert double to long PrintVal((long)d); // Explicit cast required
In this example, PrintVal
accepts a long
parameter.
Passing an int
or byte
is allowed because they are
implicitly promoted to long
. Passing a double
requires
an explicit cast, as it is a narrowing conversion.
String Conversion Methods
C# offers methods like Convert
, Parse
, and
TryParse
for converting strings to numeric types.
Parse
throws exceptions on invalid input, while
TryParse
returns a boolean indicating success, making it safer for
user input.
string validStr = "123"; string invalidStr = "abc"; // Using Parse try { int num = int.Parse(validStr); Console.WriteLine($"Parsed number: {num}"); int error = int.Parse(invalidStr); // Throws FormatException } catch (FormatException e) { Console.WriteLine($"Parse error: {e.Message}"); } // Using TryParse bool success = int.TryParse(validStr, out int result1); Console.WriteLine($"TryParse valid: Success={success}, Result={result1}"); bool failure = int.TryParse(invalidStr, out int result2); Console.WriteLine($"TryParse invalid: Success={failure}, Result={result2}");
Parse
converts "123"
to 123
but throws a
FormatException
for "abc"
. TryParse
safely handles both cases, returning true
for valid input and
false
for invalid input, with the result stored in the
out
parameter. The output shows successful and failed conversions.
Type Promotion in Expressions
C# promotes types in expressions to the widest type involved to ensure no data
loss. For example, byte
, short
, and char
are promoted to int
in arithmetic operations.
byte b = 10; short s = 20; char c = 'A'; // ASCII 65 int i = 30; long l = 100L; float f = 5.5f; // Promotions to int int result1 = b + s + c; // byte + short + char → int // Promotions to wider types long result2 = i + l; // int + long → long float result3 = l + f; // long + float → float double result4 = f + i; // float + int → double (via float) Console.WriteLine($"Result1 (int): {result1}"); Console.WriteLine($"Result2 (long): {result2}"); Console.WriteLine($"Result3 (float): {result3}"); Console.WriteLine($"Result4 (double): {result4}");
In this example, byte
, short
, and char
are promoted to int
for addition, yielding 95
(10 + 20
+ 65). Other operations promote to long
, float
, or
double
based on the widest type. The output displays the results
and their types.
Implicit conversion rules may lead to unexpected results if not understood.
byte b1 = 100; byte b2 = 100; // byte sum = b1 + b2; // Implicit conversion to int byte sum = (byte)(b1 + b2); Console.WriteLine($"Sum of {b1} and {b2} is {sum}"); Console.WriteLine(sum.GetType()); Console.WriteLine(Byte.MaxValue); Console.WriteLine(Byte.MinValue);
For example, adding two byte
values results in an int
due to implicit conversion rules. To store the result back in a byte
,
you must explicitly cast it, as shown in the example.
Source
C# Language Specification - Conversions
Mastering type conversions and promotions in C# is essential for writing efficient and error-free code. These mechanisms impact assignments, expressions, and method calls, and understanding them helps prevent subtle bugs like overflow or precision loss.
Author
List all C# tutorials.