ZetCode

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.

Program.cs
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.

Program.cs
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.

Program.cs
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.

Program.cs
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.

Program.cs
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.

Program.cs
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.

Program.cs
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.

Program.cs
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.

Program.cs
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.

Program.cs
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

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all C# tutorials.