C# Implicit and Explicit Conversion Operators
This article covers how to implement user-defined type conversions in
C# using the implicit and explicit keywords.
You will learn the syntax, key differences, best practices for safety,
and clear code examples demonstrating how to enable seamless conversion
between custom types and standard .NET types.
Understanding Conversion Operators
In C#, user-defined conversion operators allow custom classes or
structs to be converted to and from other types. These operators must
always be declared as public static.
C# provides two keywords to specify how the conversion occurs:
implicit: Invoked automatically by the compiler without requiring a cast syntax. Use this when the conversion is safe, does not lose data, and will never throw an exception.explicit: Requires a cast expression (e.g.,(TargetType)source). Use this when the conversion may lose precision, lose information, or potentially throw an exception.
Defining an Implicit Conversion Operator
An implicit conversion improves code readability by eliminating repetitive casting when the conversion is intuitive and inherently safe.
Syntax
public static implicit operator TargetType(SourceType source)
{
// conversion logic
}Example
Converting a custom Currency object to a
decimal is safe and loss-free:
public readonly struct Currency
{
public decimal Amount { get; }
public Currency(decimal amount)
{
Amount = amount;
}
// Implicit conversion from Currency to decimal
public static implicit operator decimal(Currency currency)
{
return currency.Amount;
}
}
// Usage
Currency wallet = new Currency(49.99m);
decimal value = wallet; // No cast requiredDefining an Explicit Conversion Operator
An explicit conversion warns the developer that an operation carries risk, such as truncation or a potential runtime exception.
Syntax
public static explicit operator TargetType(SourceType source)
{
// conversion logic
}Example
Converting an int to a custom Digit type
(which only accepts values from 0 to 9) can fail, making an explicit
operator necessary:
using System;
public readonly struct Digit
{
public byte Value { get; }
public Digit(byte value)
{
if (value > 9)
{
throw new ArgumentOutOfRangeException(nameof(value), "Value must be between 0 and 9.");
}
Value = value;
}
// Explicit conversion from int to Digit
public static explicit operator Digit(int number)
{
if (number < 0 || number > 9)
{
throw new InvalidCastException($"Cannot convert {number} to a single Digit.");
}
return new Digit((byte)number);
}
// Implicit conversion back to int is safe
public static implicit operator int(Digit digit) => digit.Value;
}
// Usage
int validNumber = 7;
Digit d = (Digit)validNumber; // Explicit cast required
int invalidNumber = 25;
// Throws InvalidCastException at runtime:
// Digit failedDigit = (Digit)invalidNumber; Rules and Constraints
When implementing conversion operators in C#, adhere to the following language rules:
- Type Ownership: Either the parameter type (source) or the return type (target) must be the enclosing type where the operator is declared.
- No Redundant Conversions: You cannot define conversions between a base class and a derived class, as standard inheritance-based conversions already handle those scenarios.
- No Interface Conversions: You cannot define conversion operators to or from interface types.
- No
objectConversions: Conversions directly to or fromSystem.Objectare disallowed. - Unique Signatures: You cannot define both an
implicitand anexplicitoperator for the same source and target types.