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, andTryParse - 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
objector interfaces
Unboxing โ Object โ Value
int j = (int)obj; // explicit unboxing
- Must cast to the exact original type
- Can throw
InvalidCastExceptionif wrong type
๐งช Converting Between Strings and Numbers
Using Parse
string s = "123";
int i = int.Parse(s);
double d = double.Parse("123.45");
- Throws
FormatExceptionif string is invalid
Using TryParse (Safe)
if (int.TryParse("123", out int result))
{
Console.WriteLine(result); // 123
}
- No exception; safer for user input
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
- Use
isorasoperators to avoid exceptions:
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, returnsnullif 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 conversionexplicitโ 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, andwhereconstraints 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
- Prefer implicit conversions when safe
- Avoid explicit casts unless necessary
- Use TryParse over Parse for user input
- Use
is/asinstead of direct casting for references - Check for overflows when narrowing numerics
- Document custom conversions clearly in your classes
- Avoid multiple chained casts (readability nightmare)
๐ Summary Table
| Conversion Type | Syntax | Safe? | Notes |
|---|---|---|---|
| Implicit | long l = 5; | โ | Widening, no data loss |
| Explicit | int i = (int)5.5; | โ | May lose data |
| Boxing | object o = 5; | โ | Value โ Reference |
| Unboxing | int i = (int)o; | โ | Must match exact type |
| Parse | int.Parse("123") | โ | Throws exception if invalid |
| TryParse | int.TryParse("123", out i) | โ | Safe alternative |
as | obj as string | โ | Returns null if fails |
is | if (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.