Casting and Type Conversions in C#

The Complete Guide for Beginners and Professionals (With 40+ Examples)

C# is a strongly typed language, which means every variable has a type, and operations between incompatible types require casting or conversion.

Understanding how to convert types safely and efficiently is essential for:

  • Avoiding runtime exceptions
  • Writing clean, maintainable code
  • Optimising performance
  • Working with LINQ, APIs, and interop

This guide explains:

  • The difference between implicit, explicit, and custom conversions
  • How boxing and unboxing work
  • Safe numeric conversions
  • Using Convert, Parse, and TryParse
  • How user-defined conversions work
  • Best practices and real-world examples

๐Ÿ” Implicit vs Explicit Conversions

Implicit Conversions (Safe, Automatic)

These are conversions where no data is lost. The compiler automatically converts types.

int i = 123;
long l = i;   // int โ†’ long (implicit)
float f = i;  // int โ†’ float (implicit)
  • No cast operator required
  • Always safe
  • Often called widening conversions

Explicit Conversions (Casts)

When data might be lost or conversion is not automatic, you must use a cast:

double d = 123.45;
int x = (int)d;  // explicit cast, fractional part lost
  • Can throw exceptions if conversion fails
  • Also called narrowing conversions

โšก Numeric Conversions

Implicit Widening

byte b = 255;
int i = b;       // safe
double d = i;    // safe

Explicit Narrowing

int i = 300;
byte b = (byte)i;  // unsafe: wraps around, result = 44

Tip: Always check values before narrowing to prevent overflow.


๐Ÿง  Boxing and Unboxing

Boxing โ€” Value โ†’ Object

int i = 123;
object obj = i;  // implicit boxing
  • Stores the value on the heap
  • Used when assigning value types to object or interfaces

Unboxing โ€” Object โ†’ Value

int j = (int)obj; // explicit unboxing
  • Must cast to the exact original type
  • Can throw InvalidCastException if wrong type

๐Ÿงช Converting Between Strings and Numbers

Using Parse

string s = "123";
int i = int.Parse(s);
double d = double.Parse("123.45");
  • Throws FormatException if string is invalid

Using TryParse (Safe)

if (int.TryParse("123", out int result))
{
    Console.WriteLine(result); // 123
}

Using Convert Class

string s = "123";
int i = Convert.ToInt32(s);
  • Handles null (returns 0)
  • Can convert between types beyond string โ†’ numeric

๐Ÿ”ง Reference Type Conversions

Upcasting (Implicit)

class Animal { }
class Dog : Animal { }

Dog dog = new Dog();
Animal a = dog;  // implicit upcast

Downcasting (Explicit)

Animal a = new Dog();
Dog d = (Dog)a;  // explicit downcast
if (a is Dog d2)
{
    Console.WriteLine("Safe cast!");
}

Dog d3 = a as Dog; // returns null if not Dog

๐Ÿ›  Using as and is Operators

  • is โ€” checks type before casting
if (obj is string s)
{
    Console.WriteLine(s.Length);
}
  • as โ€” attempts cast, returns null if fails
object obj = "Hello";
string str = obj as string; // safe

Tip: as only works with reference types.


๐ŸŒ User-Defined Conversions

C# allows you to define custom conversions in your classes:

class Temperature
{
    public double Celsius { get; set; }

    public static implicit operator double(Temperature t)
    {
        return t.Celsius;
    }

    public static explicit operator Temperature(double d)
    {
        return new Temperature { Celsius = d };
    }
}
  • implicit โ€” safe conversion
  • explicit โ€” requires cast

๐Ÿ“ฆ Casting with Generics

When working with generics, sometimes casts are unavoidable:

void Print<T>(T item)
{
    if (item is string s)
    {
        Console.WriteLine($"String: {s}");
    }
}
  • Combine is, as, and where constraints for safety

๐Ÿงฉ Practical Examples

Example 1: Safe Numeric Conversion

string userInput = "42";
if (int.TryParse(userInput, out int number))
{
    Console.WriteLine(number * 2);
}

Example 2: LINQ with Casting

object[] items = { "Hello", 123, 45.6 };
var strings = items.OfType<string>();  // filters only strings

Example 3: Working with Interfaces

IEnumerable<Animal> animals = new List<Dog> { new Dog() };
Dog dog = (Dog)animals.First();

โœ… Best Practices

  1. Prefer implicit conversions when safe
  2. Avoid explicit casts unless necessary
  3. Use TryParse over Parse for user input
  4. Use is / as instead of direct casting for references
  5. Check for overflows when narrowing numerics
  6. Document custom conversions clearly in your classes
  7. Avoid multiple chained casts (readability nightmare)

๐Ÿ“Š Summary Table

Conversion TypeSyntaxSafe?Notes
Implicitlong l = 5;โœ…Widening, no data loss
Explicitint i = (int)5.5;โš May lose data
Boxingobject o = 5;โœ…Value โ†’ Reference
Unboxingint i = (int)o;โš Must match exact type
Parseint.Parse("123")โš Throws exception if invalid
TryParseint.TryParse("123", out i)โœ…Safe alternative
asobj as stringโœ…Returns null if fails
isif (obj is string s)โœ…Type check + cast

Final Thoughts

Casting and type conversions in C# are essential for safe, readable, and maintainable code.

Key takeaways:

  • Understand implicit vs explicit conversions
  • Avoid unsafe casts with is / as / TryParse
  • Use custom conversions thoughtfully
  • Remember: boxing/unboxing affects performance

Mastering these concepts ensures your code is robust, performant, and professional – especially when working with APIs, LINQ, generics, or legacy systems.