Previous : Variables - Index - Next : Expressions

6. Conversions

A conversion enables an expression of one type to be treated as another type. Conversions can be implicit or explicit, and this determines whether an explicit cast is required. For instance, the conversion from type int to type long is implicit, so expressions of type int can implicitly be treated as type long. The opposite conversion, from type long to type int, is explicit and so an explicit cast is required.

int a = 123;
long b = a;       // implicit conversion from int to long
int c = (int) b;  // explicit conversion from long to int

Some conversions are defined by the language. Programs may also define their own conversions (§6.4).

6.1 Implicit conversions

The following conversions are classified as implicit conversions:

·         Identity conversions

·         Implicit numeric conversions

·         Implicit enumeration conversions.

·         Implicit reference conversions

·         Boxing conversions

·         Implicit constant expression conversions

·         User-defined implicit conversions

Implicit conversions can occur in a variety of situations, including function member invocations (§7.4.3), cast expressions (§7.6.6), and assignments (§7.13).

The pre-defined implicit conversions always succeed and never cause exceptions to be thrown. Properly designed user-defined implicit conversions should exhibit these characteristics as well.

6.1.1 Identity conversion

An identity conversion converts from any type to the same type. This conversion exists only such that an entity that already has a required type can be said to be convertible to that type.

6.1.2 Implicit numeric conversions

The implicit numeric conversions are:

·         From sbyte to short, int, long, float, double, or decimal.

·         From byte to short, ushort, int, uint, long, ulong, float, double, or decimal.

·         From short to int, long, float, double, or decimal.

·         From ushort to int, uint, long, ulong, float, double, or decimal.

·         From int to long, float, double, or decimal.

·         From uint to long, ulong, float, double, or decimal.

·         From long to float, double, or decimal.

·         From ulong to float, double, or decimal.

·         From char to ushort, int, uint, long, ulong, float, double, or decimal.

·         From float to double.

Conversions from int, uint, long, or ulong to float and from long or ulong to double may cause a loss of precision, but will never cause a loss of magnitude. The other implicit numeric conversions never lose any information.

There are no implicit conversions to the char type, so values of the other integral types do not automatically convert to the char type.

6.1.3 Implicit enumeration conversions

An implicit enumeration conversion permits the decimal-integer-literal 0 to be converted to any enum-type.

6.1.4 Implicit reference conversions

The implicit reference conversions are:

·         From any reference-type to object.

·         From any class-type S to any class-type T, provided S is derived from T.

·         From any class-type S to any interface-type T, provided S implements T.

·         From any interface-type S to any interface-type T, provided S is derived from T.

·         From an array-type S with an element type SE to an array-type T with an element type TE, provided all of the following are true:

o        S and T differ only in element type. In other words, S and T have the same number of dimensions.

o        Both SE and TE are reference-types.

o        An implicit reference conversion exists from SE to TE.

·         From any array-type to System.Array.

·         From any delegate-type to System.Delegate.

·         From the null type to any reference-type.

The implicit reference conversions are those conversions between reference-types that can be proven to always succeed, and therefore require no checks at run-time.

Reference conversions, implicit or explicit, never change the referential identity of the object being converted. In other words, while a reference conversion may change the type of the reference, it never changes the type or value of the object being referred to.

6.1.5 Boxing conversions

A boxing conversion permits any value-type to be implicitly converted to type object or System.ValueType or to any interface-type implemented by the value-type. Boxing a value of a value-type consists of allocating an object instance and copying the value-type value into that instance. A struct can be boxed to the type System.ValueType, since that is a base class for all structs (§11.3.2).

Boxing conversions are described further in §4.3.1.

6.1.6 Implicit constant expression conversions

An implicit constant expression conversion permits the following conversions:

·         A constant-expression7.15) of type int can be converted to type sbyte, byte, short, ushort, uint, or ulong, provided the value of the constant-expression is within the range of the destination type.

·         A constant-expression of type long can be converted to type ulong, provided the value of the constant-expression is not negative.

6.1.7 User-defined implicit conversions

A user-defined implicit conversion consists of an optional standard implicit conversion, followed by execution of a user-defined implicit conversion operator, followed by another optional standard implicit conversion. The exact rules for evaluating user-defined conversions are described in §6.4.3.

6.2 Explicit conversions

The following conversions are classified as explicit conversions:

·         All implicit conversions.

·         Explicit numeric conversions.

·         Explicit enumeration conversions.

·         Explicit reference conversions.

·         Explicit interface conversions.

·         Unboxing conversions.

·         User-defined explicit conversions.

Explicit conversions can occur in cast expressions (§7.6.6).

The set of explicit conversions includes all implicit conversions. This means that redundant cast expressions are allowed.

The explicit conversions that are not implicit conversions are conversions that cannot be proven to always succeed, conversions that are known to possibly lose information, and conversions across domains of types sufficiently different to merit explicit notation.

6.2.1 Explicit numeric conversions

The explicit numeric conversions are the conversions from a numeric-type to another numeric-type for which an implicit numeric conversion (§6.1.2) does not already exist:

·         From sbyte to byte, ushort, uint, ulong, or char.

·         From byte to sbyte and char.

·         From short to sbyte, byte, ushort, uint, ulong, or char.

·         From ushort to sbyte, byte, short, or char.

·         From int to sbyte, byte, short, ushort, uint, ulong, or char.

·         From uint to sbyte, byte, short, ushort, int, or char.

·         From long to sbyte, byte, short, ushort, int, uint, ulong, or char.

·         From ulong to sbyte, byte, short, ushort, int, uint, long, or char.

·         From char to sbyte, byte, or short.

·         From float to sbyte, byte, short, ushort, int, uint, long, ulong, char, or decimal.

·         From double to sbyte, byte, short, ushort, int, uint, long, ulong, char, float, or decimal.

·         From decimal to sbyte, byte, short, ushort, int, uint, long, ulong, char, float, or double.

Because the explicit conversions include all implicit and explicit numeric conversions, it is always possible to convert from any numeric-type to any other numeric-type using a cast expression (§7.6.6).

The explicit numeric conversions possibly lose information or possibly cause exceptions to be thrown. An explicit numeric conversion is processed as follows:

·         For a conversion from an integral type to another integral type, the processing depends on the overflow checking context (§7.5.12) in which the conversion takes place:

o        In a checked context, the conversion succeeds if the value of the source operand is within the range of the destination type, but throws a System.OverflowException if the value of the source operand is outside the range of the destination type.

o        In an unchecked context, the conversion always succeeds, and proceeds as follows.

·         If the source type is larger than the destination type, then the source value is truncated by discarding its “extra” most significant bits. The result is then treated as a value of the destination type.

·         If the source type is smaller than the destination type, then the source value is either sign-extended or zero-extended so that it is the same size as the destination type. Sign-extension is used if the source type is signed; zero-extension is used if the source type is unsigned. The result is then treated as a value of the destination type.

·         If the source type is the same size as the destination type, then the source value is treated as a value of the destination type.

·         For a conversion from decimal to an integral type, the source value is rounded towards zero to the nearest integral value, and this integral value becomes the result of the conversion. If the resulting integral value is outside the range of the destination type, a System.OverflowException is thrown.

·         For a conversion from float or double to an integral type, the processing depends on the overflow checking context (§7.5.12) in which the conversion takes place:

o        In a checked context, the conversion proceeds as follows:

·         If the value of the operand is NaN or infinite, a System.OverflowException is thrown.

·         Otherwise, the source operand is rounded towards zero to the nearest integral value. If this integral value is within the range of the destination type then this value is the result of the conversion.

·         Otherwise, a System.OverflowException is thrown.

o        In an unchecked context, the conversion always succeeds, and proceeds as follows.

·         If the value of the operand is NaN or infinite, the result of the conversion is an unspecified value of the destination type.

·         Otherwise, the source operand is rounded towards zero to the nearest integral value. If this integral value is within the range of the destination type then this value is the result of the conversion.

·         Otherwise, the result of the conversion is an unspecified value of the destination type.

·         For a conversion from double to float, the double value is rounded to the nearest float value. If the double value is too small to represent as a float, the result becomes positive zero or negative zero. If the double value is too large to represent as a float, the result becomes positive infinity or negative infinity. If the double value is NaN, the result is also NaN.

·         For a conversion from float or double to decimal, the source value is converted to decimal representation and rounded to the nearest number after the 28th decimal place if required (§4.1.7). If the source value is too small to represent as a decimal, the result becomes zero. If the source value is NaN, infinity, or too large to represent as a decimal, a System.OverflowException is thrown.

·         For a conversion from decimal to float or double, the decimal value is rounded to the nearest double or float value. While this conversion may lose precision, it never causes an exception to be thrown.

6.2.2 Explicit enumeration conversions

The explicit enumeration conversions are:

·         From sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, or decimal to any enum-type.

·         From any enum-type to sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, or decimal.

·         From any enum-type to any other enum-type.

An explicit enumeration conversion between two types is processed by treating any participating enum-type as the underlying type of that enum-type, and then performing an implicit or explicit numeric conversion between the resulting types. For example, given an enum-type E with and underlying type of int, a conversion from E to byte is processed as an explicit numeric conversion (§6.2.1) from int to byte, and a conversion from byte to E is processed as an implicit numeric conversion (§6.1.2) from byte to int.

6.2.3 Explicit reference conversions

The explicit reference conversions are:

·         From object to any other reference-type.

·         From any class-type S to any class-type T, provided S is a base class of T.

·         From any class-type S to any interface-type T, provided S is not sealed and provided S does not implement T.

·         From any interface-type S to any class-type T, provided T is not sealed or provided T implements S.

·         From any interface-type S to any interface-type T, provided S is not derived from T.

·         From an array-type S with an element type SE to an array-type T with an element type TE, provided all of the following are true:

o        S and T differ only in element type. In other words, S and T have the same number of dimensions.

o        Both SE and TE are reference-types.

o        An explicit reference conversion exists from SE to TE.

·         From System.Array and the interfaces it implements to any array-type.

·         From System.Delegate and the interfaces it implements to any delegate-type.

The explicit reference conversions are those conversions between reference-types that require run-time checks to ensure they are correct.

For an explicit reference conversion to succeed at run-time, the value of the source operand must be null, or the actual type of the object referenced by the source operand must be a type that can be converted to the destination type by an implicit reference conversion (§6.1.4). If an explicit reference conversion fails, a System.InvalidCastException is thrown.

Reference conversions, implicit or explicit, never change the referential identity of the object being converted. In other words, while a reference conversion may change the type of the reference, it never changes the type or value of the object being referred to.

6.2.4 Unboxing conversions

An unboxing conversion permits an explicit conversion from type object or System.ValueType to any value-type, or from any interface-type to any value-type that implements the interface-type. An unboxing operation consists of first checking that the object instance is a boxed value of the given value-type, and then copying the value out of the instance. A struct can be unboxed from the type System.ValueType, since that is a base class for all structs (§11.3.2).

Unboxing conversions are described further in §4.3.2.

6.2.5 User-defined explicit conversions

A user-defined explicit conversion consists of an optional standard explicit conversion, followed by execution of a user-defined implicit or explicit conversion operator, followed by another optional standard explicit conversion. The exact rules for evaluating user-defined conversions are described in §6.4.4.

6.3 Standard conversions

The standard conversions are those pre-defined conversions that can occur as part of a user-defined conversion.

6.3.1 Standard implicit conversions

The following implicit conversions are classified as standard implicit conversions:

·         Identity conversions (§6.1.1)

·         Implicit numeric conversions (§6.1.2)

·         Implicit reference conversions (§6.1.4)

·         Boxing conversions (§6.1.5)

·         Implicit constant expression conversions (§6.1.6)

The standard implicit conversions specifically exclude user-defined implicit conversions.

6.3.2 Standard explicit conversions

The standard explicit conversions are all standard implicit conversions plus the subset of the explicit conversions for which an opposite standard implicit conversion exists. In other words, if a standard implicit conversion exists from a type A to a type B, then a standard explicit conversion exists from type A to type B and from type B to type A.

6.4 User-defined conversions

C# allows the pre-defined implicit and explicit conversions to be augmented by user-defined conversions. User-defined conversions are introduced by declaring conversion operators (§10.9.3) in class and struct types.

6.4.1 Permitted user-defined conversions

C# permits only certain user-defined conversions to be declared. In particular, it is not possible to redefine an already existing implicit or explicit conversion. A class or struct is permitted to declare a conversion from a source type S to a target type T only if all of the following are true:

·         S and T are different types.

·         Either S or T is the class or struct type in which the operator declaration takes place.

·         Neither S nor T is object or an interface-type.

·         T is not a base class of S, and S is not a base class of T.

The restrictions that apply to user-defined conversions are discussed further in §10.9.3.

6.4.2 Evaluation of user-defined conversions

A user-defined conversion converts a value from its type, called the source type, to another type, called the target type. Evaluation of a user-defined conversion centers on finding the most specific user-defined conversion operator for the particular source and target types. This determination is broken into several steps:

·         Finding the set of classes and structs from which user-defined conversion operators will be considered. This set consists of the source type and its base classes and the target type and its base classes (with the implicit assumptions that only classes and structs can declare user-defined operators, and that non-class types have no base classes).

·         From that set of types, determining which user-defined conversion operators are applicable. For a conversion operator to be applicable, it must be possible to perform a standard conversion (§6.3) from the source type to the operand type of the operator, and it must be possible to perform a standard conversion from the result type of the operator to the target type.

·         From the set of applicable user-defined operators, determining which operator is unambiguously the most specific. In general terms, the most specific operator is the operator whose operand type is “closest” to the source type and whose result type is “closest” to the target type. The exact rules for establishing the most specific user-defined conversion operator are defined in the following sections.

Once a most specific user-defined conversion operator has been identified, the actual execution of the user-defined conversion involves up to three steps:

·         First, if required, performing a standard conversion from the source type to the operand type of the user-defined conversion operator.

·         Next, invoking the user-defined conversion operator to perform the conversion.

·         Finally, if required, performing a standard conversion from the result type of the user-defined conversion operator to the target type.

Evaluation of a user-defined conversion never involves more than one user-defined conversion operator. In other words, a conversion from type S to type T will never first execute a user-defined conversion from S to X and then execute a user-defined conversion from X to T.

Exact definitions of evaluation of user-defined implicit or explicit conversions are given in the following sections. The definitions make use of the following terms:

·         If a standard implicit conversion (§6.3.1) exists from a type A to a type B, and if neither A nor B are interface-types, then A is said to be encompassed by B, and B is said to encompass A.

·         The most encompassing type in a set of types is the one type that encompasses all other types in the set. If no single type encompasses all other types, then the set has no most encompassing type. In more intuitive terms, the most encompassing type is the “largest” type in the set—the one type to which each of the other types can be implicitly converted.

·         The most encompassed type in a set of types is the one type that is encompassed by all other types in the set. If no single type is encompassed by all other types, then the set has no most encompassed type. In more intuitive terms, the most encompassed type is the “smallest” type in the set—the one type that can be implicitly converted to each of the other types.

6.4.3 User-defined implicit conversions

A user-defined implicit conversion from type S to type T is processed as follows:

·         Find the set of types, D, from which user-defined conversion operators will be considered. This set consists of S (if S is a class or struct), the base classes of S (if S is a class), and T (if T is a class or struct).

·         Find the set of applicable user-defined conversion operators, U. This set consists of the user-defined implicit conversion operators declared by the classes or structs in D that convert from a type encompassing S to a type encompassed by T. If U is empty, the conversion is undefined and a compile-time error occurs.

·         Find the most specific source type, SX, of the operators in U:

o        If any of the operators in U convert from S, then SX is S.

o        Otherwise, SX is the most encompassed type in the combined set of source types of the operators in U. If no most encompassed type can be found, then the conversion is ambiguous and a compile-time error occurs.

·         Find the most specific target type, TX, of the operators in U:

o        If any of the operators in U convert to T, then TX is T.

o        Otherwise, TX is the most encompassing type in the combined set of target types of the operators in U. If no most encompassing type can be found, then the conversion is ambiguous and a compile-time error occurs.

·         If U contains exactly one user-defined conversion operator that converts from SX to TX, then this is the most specific conversion operator. If no such operator exists, or if more than one such operator exists, then the conversion is ambiguous and a compile-time error occurs. Otherwise, the user-defined conversion is applied:

o        If S is not SX, then a standard implicit conversion from S to SX is performed.

o        The most specific user-defined conversion operator is invoked to convert from SX to TX.

o        If TX is not T, then a standard implicit conversion from TX to T is performed.

6.4.4 User-defined explicit conversions

A user-defined explicit conversion from type S to type T is processed as follows:

·         Find the set of types, D, from which user-defined conversion operators will be considered. This set consists of S (if S is a class or struct), the base classes of S (if S is a class), T (if T is a class or struct), and the base classes of T (if T is a class).

·         Find the set of applicable user-defined conversion operators, U. This set consists of the user-defined implicit or explicit conversion operators declared by the classes or structs in D that convert from a type encompassing or encompassed by S to a type encompassing or encompassed by T. If U is empty, the conversion is undefined and a compile-time error occurs.

·         Find the most specific source type, SX, of the operators in U:

o        If any of the operators in U convert from S, then SX is S.

o        Otherwise, if any of the operators in U convert from types that encompass S, then SX is the most encompassed type in the combined set of source types of those operators. If no most encompassed type can be found, then the conversion is ambiguous and a compile-time error occurs.

o        Otherwise, SX is the most encompassing type in the combined set of source types of the operators in U. If no most encompassing type can be found, then the conversion is ambiguous and a compile-time error occurs.

·         Find the most specific target type, TX, of the operators in U:

o        If any of the operators in U convert to T, then TX is T.

o        Otherwise, if any of the operators in U convert to types that are encompassed by T, then TX is the most encompassing type in the combined set of target types of those operators. If no most encompassing type can be found, then the conversion is ambiguous and a compile-time error occurs.

o        Otherwise, TX is the most encompassed type in the combined set of target types of the operators in U. If no most encompassed type can be found, then the conversion is ambiguous and a compile-time error occurs.

·         If U contains exactly one user-defined conversion operator that converts from SX to TX, then this is the most specific conversion operator. If no such operator exists, or if more than one such operator exists, then the conversion is ambiguous and a compile-time error occurs. Otherwise, the user-defined conversion is applied:

o        If S is not SX, then a standard explicit conversion from S to SX is performed.

o        The most specific user-defined conversion operator is invoked to convert from SX to TX.

o        If TX is not T, then a standard explicit conversion from TX to T is performed.