C# casting and type conversion — explicit cast, as, is, Convert, implicit
Type conversion in C# comes in several forms: implicit conversions (automatic, always safe), explicit casts (manual, may throw), the as/is operators (safe patterns), and helper methods like Convert.ToInt32(). Choosing the right one avoids both silent data loss and unexpected exceptions.
Implicit Conversion
The compiler inserts implicit conversions automatically when there is no risk of data loss. No cast syntax is needed.
int i = 42;
long l = i; // int → long (widening, always safe)
float f = i; // int → float (safe, minor precision change)
double d = i; // int → double
Console.WriteLine(l); // 42
Console.WriteLine(d); // 42
// Also works for derived → base class
object obj = "hello"; // string → object (always valid)Explicit Cast
Use explicit cast (Type)value when converting a wider type to a narrower one. If the value doesn't fit, a OverflowException or data loss occurs.
double d = 9.99;
int i = (int)d; // truncates — no rounding
Console.WriteLine(i); // 9
long big = 3_000_000_000L;
int small = (int)big; // overflows silently (unchecked context)
Console.WriteLine(small); // -1294967296 (garbage!)
// Use checked to throw instead of silently overflow:
try
{
int safe = checked((int)big);
}
catch (OverflowException ex)
{
Console.WriteLine($"Overflow: {ex.Message}");
}as Operator — Safe Reference Cast
as attempts the cast and returns null if it fails, instead of throwing. Only works for reference types and nullable value types.
object obj = "Hello, world!";
string? s = obj as string;
if (s != null)
Console.WriteLine(s.Length); // 13
// Returns null for incompatible types — no exception
object num = 42;
string? fail = num as string;
Console.WriteLine(fail); // (null)is Operator — Pattern Check
is tests the type without throwing. Combine it with a pattern variable to cast and bind in one step.
object[] items = { "text", 42, 3.14, true, "another" };
foreach (var item in items)
{
if (item is string s)
Console.WriteLine($"String of length {s.Length}: {s}");
else if (item is int n)
Console.WriteLine($"Integer: {n}");
else
Console.WriteLine($"Other: {item}");
}
// Output:
// String of length 4: text
// Integer: 42
// Other: 3.14
// Other: True
// String of length 7: anotheras vs Direct Cast
(T)value | value as T | |
|---|---|---|
| On failure | Throws InvalidCastException | Returns null |
| Works with value types | Yes | No (only reference/nullable) |
| Performance | Slightly faster | One null check |
| Use when | You're certain of the type | Type might not match |
object obj = "hello";
// Direct cast — throws if wrong type
string direct = (string)obj;
// as cast — returns null if wrong type
string? safe = obj as string;Convert Class
System.Convert provides explicit conversions between base types and handles null by returning the default value (unlike a direct cast which throws).
string s = "42";
int i = Convert.ToInt32(s); // "42" → 42
double d = Convert.ToDouble("3.14"); // "3.14" → 3.14
bool b = Convert.ToBoolean("true"); // "true" → true
// Convert.ToInt32(null) returns 0; (int)null would throw
object? nullObj = null;
int zero = Convert.ToInt32(nullObj);
Console.WriteLine(zero); // 0
Convert.ToInt32()rounds —Convert.ToInt32(3.7)→4. The explicit cast(int)3.7truncates →3.
Parse vs Convert vs Cast
string s = "100";
// Parse — string → value type, throws on failure
int a = int.Parse(s);
// TryParse — safe, returns bool
int b;
bool ok = int.TryParse(s, out b);
// Convert — handles null, uses rounding for floats
int c = Convert.ToInt32(s);
// Cast — only between numeric types, not from string
double d = 100.9;
int e = (int)d; // truncates to 100User-Defined Conversions
Classes can define implicit and explicit conversion operators.
public class Celsius
{
public double Value { get; }
public Celsius(double value) => Value = value;
// Explicit: caller must write (Fahrenheit)celsius
public static explicit operator Fahrenheit(Celsius c)
=> new Fahrenheit(c.Value * 9 / 5 + 32);
public override string ToString() => $"{Value}°C";
}
public class Fahrenheit
{
public double Value { get; }
public Fahrenheit(double value) => Value = value;
// Implicit: assignment is automatic
public static implicit operator Celsius(Fahrenheit f)
=> new Celsius((f.Value - 32) * 5 / 9);
public override string ToString() => $"{Value}°F";
}
var boiling = new Celsius(100);
var fahr = (Fahrenheit)boiling; // explicit required
Console.WriteLine(fahr); // 212°F
Fahrenheit body = new Fahrenheit(98.6);
Celsius c = body; // implicit — no cast needed
Console.WriteLine(c); // 37°CCasting in Generics
Use as or is when working with unconstrained generic types.
public static T? TryCast<T>(object obj) where T : class
=> obj as T;
string? s = TryCast<string>("hello"); // "hello"
string? n = TryCast<string>(42); // null
Console.WriteLine(s); // hello
Console.WriteLine(n); // (null)Quick Reference
| Method | Safe? | Null handling | Rounds or truncates |
|---|---|---|---|
(T)value | No — throws InvalidCastException | Throws | Truncates |
as T | Yes — returns null | Returns null | N/A (reference) |
is T x | Yes — returns bool | Returns false | N/A |
Convert.ToX() | Throws on bad input | Returns default | Rounds |
T.Parse() | Throws on bad input | Throws | N/A |
T.TryParse() | Yes — returns bool | Returns false | N/A |