The standard conversions are those pre-defined conversions that can occur as part of a user-defined conversion.
6.3.1Standard implicit conversions
The following implicit conversions are classified as standard implicit conversions:
-
Identity conversions (§6.1.1)
-
Implicit numeric conversions (§6.1.2)
-
Implicit nullable conversions (§6.1.4)
-
Implicit reference conversions (§6.1.6)
-
Boxing conversions (§6.1.7)
-
Implicit constant expression conversions (§6.1.8)
-
Implicit conversions involving type parameters (§6.1.9)
The standard implicit conversions specifically exclude user-defined implicit conversions.
6.3.2Standard 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.4User-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.10.3) in class and struct types.
6.4.1Permitted 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.
For a given source type S and target type T, if S or T are nullable types, let S0 and T0 refer to their underlying types, otherwise S0 and T0 are equal to S and T respectively. 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:
-
S0 and T0 are different types.
-
Either S0 or T0 is the class or struct type in which the operator declaration takes place.
-
Neither S0 nor T0 is an interface-type.
-
Excluding user-defined conversions, a conversion does not exist from S to T or from T to S.
The restrictions that apply to user-defined conversions are discussed further in §10.10.3.
6.4.2Lifted conversion operators
Given a user-defined conversion operator that converts from a non-nullable value type S to a non-nullable value type T, a lifted conversion operator exists that converts from S? to T?. This lifted conversion operator performs an unwrapping from S? to S followed by the user-defined conversion from S to T followed by a wrapping from T to T?, except that a null valued S? converts directly to a null valued T?.
A lifted conversion operator has the same implicit or explicit classification as its underlying user-defined conversion operator. The term “user-defined conversion” applies to the use of both user-defined and lifted conversion operators.
6.4.3Evaluation 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). For the purposes of this step, if either the source or target type is a nullable-type, their underlying type is used instead.
-
From that set of types, determining which user-defined and lifted 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. User-defined conversion operators are preferred over lifted conversion operators. 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 or lifted conversion operator.
-
Next, invoking the user-defined or lifted conversion operator to perform the conversion.
-
Finally, if required, performing a standard conversion from the result type of the user-defined or lifted conversion operator to the target type.
Evaluation of a user-defined conversion never involves more than one user-defined or lifted 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.4User-defined implicit conversions
A user-defined implicit conversion from type S to type T is processed as follows:
-
Determine the types S0 and T0. If S or T are nullable types, S0 and T0 are their underlying types, otherwise S0 and T0 are equal to S and T respectively.
-
Find the set of types, D, from which user-defined conversion operators will be considered. This set consists of S0 (if S0 is a class or struct), the base classes of S0 (if S0 is a class), and T0 (if T0 is a class or struct).
-
Find the set of applicable user-defined and lifted conversion operators, U. This set consists of the user-defined and lifted 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:
-
If any of the operators in U convert from S, then SX is S.
-
Otherwise, SX is the most encompassed type in the combined set of source types of the operators in U. If exactly one most encompassed type cannot 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:
-
If any of the operators in U convert to T, then TX is T.
-
Otherwise, TX is the most encompassing type in the combined set of target types of the operators in U. If exactly one most encompassing type cannot be found, then the conversion is ambiguous and a compile-time error occurs.
-
Find the most specific conversion operator:
-
If U contains exactly one user-defined conversion operator that converts from SX to TX, then this is the most specific conversion operator.
-
Otherwise, if U contains exactly one lifted conversion operator that converts from SX to TX, then this is the most specific conversion operator.
-
Otherwise, the conversion is ambiguous and a compile-time error occurs.
-
Finally, apply the conversion:
-
If S is not SX, then a standard implicit conversion from S to SX is performed.
-
The most specific conversion operator is invoked to convert from SX to TX.
-
If TX is not T, then a standard implicit conversion from TX to T is performed.
6.4.5User-defined explicit conversions
A user-defined explicit conversion from type S to type T is processed as follows:
-
Determine the types S0 and T0. If S or T are nullable types, S0 and T0 are their underlying types, otherwise S0 and T0 are equal to S and T respectively.
-
Find the set of types, D, from which user-defined conversion operators will be considered. This set consists of S0 (if S0 is a class or struct), the base classes of S0 (if S0 is a class), T0 (if T0 is a class or struct), and the base classes of T0 (if T0 is a class).
-
Find the set of applicable user-defined and lifted conversion operators, U. This set consists of the user-defined and lifted 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:
-
If any of the operators in U convert from S, then SX is S.
-
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.
-
Otherwise, SX is the most encompassing type in the combined set of source types of the operators in U. If exactly one most encompassing type cannot 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:
-
If any of the operators in U convert to T, then TX is T.
-
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 exactly one most encompassing type cannot be found, then the conversion is ambiguous and a compile-time error occurs.
-
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.
-
Find the most specific conversion operator:
-
If U contains exactly one user-defined conversion operator that converts from SX to TX, then this is the most specific conversion operator.
-
Otherwise, if U contains exactly one lifted conversion operator that converts from SX to TX, then this is the most specific conversion operator.
-
Otherwise, the conversion is ambiguous and a compile-time error occurs.
-
Finally, apply the conversion:
-
If S is not SX, then a standard explicit conversion from S to SX is performed.
-
The most specific user-defined conversion operator is invoked to convert from SX to TX.
-
If TX is not T, then a standard explicit conversion from TX to T is performed.
Share with your friends: |