10 Conversiones
10.1 General
Una conversión hace que una expresión se convierta o se trate como de un tipo determinado; en el primer caso, una conversión puede implicar un cambio en la representación. Las conversiones pueden ser implícitas o explícitas y esto determina si se requiere una conversión explícita.
Ejemplo: por ejemplo, la conversión de tipo
int
a tipolong
es implícita, por lo que las expresiones de tipoint
se pueden tratar implícitamente como tipolong
. La conversión opuesta, de tipolong
a tipoint
, es explícita y, por tanto, se requiere una conversión explícita.int a = 123; long b = a; // implicit conversion from int to long int c = (int) b; // explicit conversion from long to int
ejemplo final
Algunas conversiones se definen mediante el idioma. Los programas también pueden definir sus propias conversiones (§10.5).
Algunas conversiones del lenguaje se definen de expresiones a tipos, otras de tipos a tipos. Una conversión de un tipo se aplica a todas las expresiones que tienen ese tipo.
Ejemplo:
enum Color { Red, Blue, Green } // The expression 0 converts implicitly to enum types Color c0 = 0; // Other int expressions need explicit conversion Color c1 = (Color)1; // Conversion from null expression (no type) to string string x = null; // Conversion from lambda expression to delegate type Func<int, int> square = x => x * x;
ejemplo final
10.2 Conversiones implícitas
10.2.1 General
Las conversiones siguientes se clasifican como conversiones implícitas:
- Conversiones de identidad (§10.2.2)
- Conversiones numéricas implícitas (§10.2.3)
- Conversiones de enumeración implícitas (§10.2.4)
- Conversiones implícitas de cadenas interpoladas (§10.2.5)
- Conversiones de referencia implícitas (§10.2.8)
- Conversiones de boxeo (§10.2.9)
- Conversiones dinámicas implícitas (§10.2.10)
- Conversiones implícitas de parámetros de tipo (§10.2.12)
- Conversiones implícitas de expresiones constantes (§10.2.11)
- Conversiones implícitas definidas por el usuario (incluida la elevación) (§10.2.14)
- Conversiones de funciones anónimas (§10.2.15)
- Conversiones de grupo de métodos (§10.2.15)
- Conversiones de literales NULL (§10.2.7)
- Conversiones implícitas que aceptan valores NULL (§10.2.6)
- Conversiones implícitas de tupla (§10.2.13)
- Conversiones literales predeterminadas (§10.2.16)
- Conversiones de inicio implícitas (§10.2.17)
Las conversiones implícitas pueden producirse en diversas situaciones, incluidas las invocaciones de miembro de función (§12.6.6), expresiones de conversión (§12.9.7) y asignaciones (§12.21).
Las conversiones implícitas predefinidas siempre se realizan correctamente y nunca hacen que se produzcan excepciones.
Nota: Las conversiones implícitas definidas por el usuario correctamente diseñadas también deben mostrar estas características. nota final
Para la conversión, los tipos object
y dynamic
son convertibles de identidad (§10.2.2).
Sin embargo, las conversiones dinámicas (§10.2.10) solo se aplican a expresiones de tipo dynamic
(§8.2.4).
10.2.2 Conversión de identidad
Una conversión de identidad se convierte de cualquier tipo al mismo tipo o a un tipo equivalente en tiempo de ejecución. Una razón por la que existe esta conversión es que se puede decir que un tipo T
o una expresión de tipo T
se pueden convertir a T
sí mismos. Existen las siguientes conversiones de identidad:
- Entre
T
yT
, para cualquier tipoT
. - Entre
T
yT?
para cualquier tipoT
de referencia . - Entre
object
ydynamic
. - Entre todos los tipos de tupla con la misma aridad y el tipo construido
ValueTuple<...>
correspondiente, cuando existe una conversión de identidad entre cada par de tipos de elementos correspondientes. - Entre los tipos construidos a partir del mismo tipo genérico donde existe una conversión de identidad entre cada argumento de tipo correspondiente.
Ejemplo: a continuación se muestra la naturaleza recursiva de la tercera regla:
(int a , string b) t1 = (1, "two"); (int c, string d) t2 = (3, "four"); // Identity conversions exist between // the types of t1, t2, and t3. var t3 = (5, "six"); t3 = t2; t2 = t1; var t4 = (t1, 7); var t5 = (t2, 8); // Identity conversions exist between // the types of t4, t5, and t6. var t6 =((8, "eight"), 9); t6 = t5; t5 = t4;
Los tipos de tuplas
t1
t2
yt3
todos tienen dos elementos: unint
seguido de .string
Los tipos de elementos de tupla pueden usarse por tuplas, como ent4
,t5
yt6
. Existe una conversión de identidad entre cada par de tipos de elementos correspondientes, incluidas las tuplas anidadas, por lo que existe una conversión de identidad entre los tipos de tuplast4
,t5
yt6
.ejemplo final
Todas las conversiones de identidad son simétricas. Si existe una conversión de identidad desde T₁
a T₂
, existe una conversión de identidad de T₂
a T₁
. Dos tipos son la identidad convertible cuando existe una conversión de identidad entre dos tipos.
En la mayoría de los casos, una conversión de identidad no tiene ningún efecto en tiempo de ejecución. Sin embargo, dado que las operaciones de punto flotante se pueden realizar con una precisión mayor de la indicada por su tipo (§8.3.7), la asignación de sus resultados puede dar lugar a una pérdida de precisión y se garantiza que las conversiones explícitas reduzcan la precisión a lo que prescribe el tipo (§12.9.7).
10.2.3 Conversiones numéricas implícitas
Las conversiones numéricas implícitas son:
- De
sbyte
ashort
,int
,long
,float
,double
odecimal
. - De
byte
ashort
,ushort
, ,uint
int
,long
,ulong
,float
double
odecimal
. - De
short
aint
,long
,float
,double
odecimal
. - De
ushort
aint
, ,long
uint
,ulong
,float
,double
odecimal
. - De
int
along
,float
,double
odecimal
. - De
uint
along
,ulong
,float
,double
odecimal
. - De
long
afloat
,double
odecimal
. - De
ulong
afloat
,double
odecimal
. - De
char
aushort
,int
, ,long
uint
,ulong
,float
, ,double
odecimal
. - De
float
adouble
.
Las conversiones de , long
uint
o ulong
a float
y desde long
int
o ulong
a double
pueden provocar una pérdida de precisión, pero nunca provocarán una pérdida de magnitud. El resto de conversiones numéricas implícitas nunca pierden información.
No hay conversiones implícitas predefinidas al char
tipo, por lo que los valores de los otros tipos enteros no se convierten automáticamente en el char
tipo.
10.2.4 Conversiones implícitas de enumeración
Una conversión de enumeración implícita permite convertir un constant_expression (§12.23) con cualquier tipo entero y el valor cero que se convertirá en cualquier enum_type y en cualquier nullable_value_type cuyo tipo subyacente sea un enum_type. En este último caso, la conversión se evalúa convirtiendo en el enum_type subyacente y ajustando el resultado (§8.3.12).
10.2.5 Conversiones implícitas de cadenas interpoladas
Una conversión de cadena interpolada implícita permite convertir una interpolated_string_expression (§12.8.3) en System.IFormattable
o System.FormattableString
(que implementa System.IFormattable
).
Cuando se aplica esta conversión, un valor de cadena no se compone de la cadena interpolada. En su lugar, se crea una instancia de System.FormattableString
, como se describe más adelante en §12.8.3.
10.2.6 Conversiones implícitas que aceptan valores NULL
Las conversiones implícitas que aceptan valores NULL son las conversiones que aceptan valores NULL (§10.6.1) derivadas de conversiones predefinidas implícitas.
10.2.7 Conversiones literales null
Existe una conversión implícita del null
literal a cualquier tipo de referencia o tipo de valor que acepta valores NULL. Esta conversión genera una referencia nula si el tipo de destino es un tipo de referencia o el valor NULL (§8.3.12) del tipo de valor que acepta valores NULL especificado.
10.2.8 Conversiones de referencia implícitas
Las conversiones de referencia implícitas son:
- De cualquier reference_type a
object
ydynamic
. - De cualquier class_type
S
a cualquier class_typeT
, se derivaS
deT
. - Desde cualquier class_type a cualquier interface_type
S
T
, proporcionadoS
implementa .T
- De cualquier interface_type
S
a cualquier interface_typeT
, se derivaS
deT
. - De un array_type
S
con un tipoSᵢ
de elemento a un array_typeT
con un tipoTᵢ
de elemento , siempre que se cumplan todas las siguientes condiciones:S
yT
solo difieren en el tipo de elemento. En otras palabras,S
yT
tienen el mismo número de dimensiones.- Existe una conversión de referencia implícita de
Sᵢ
aTᵢ
.
- Desde un tipo
S[]
de matriz unidimensional aSystem.Collections.Generic.IList<T>
,System.Collections.Generic.IReadOnlyList<T>
y sus interfaces base, siempre que haya una identidad implícita o una conversión de referencia deS
aT
. - Desde cualquier array_type hacia
System.Array
y las interfaces que implementa. - Desde cualquier delegate_type hacia
System.Delegate
y las interfaces que implementa. - Desde el literal null (§6.4.5.7) a cualquier tipo de referencia.
- De cualquier reference_type a un reference_type
T
si tiene una conversión implícita de identidad o referencia a un reference_typeT₀
yT₀
tiene una conversión de identidad aT
. - De cualquier reference_type a un tipo
T
de interfaz o delegado si tiene una conversión implícita de identidad o referencia a un tipoT₀
de interfaz o delegado yT₀
es convertible de varianza (§18.2.3.3) aT
. - Conversiones implícitas que implican parámetros de tipo que se sabe que son tipos de referencia. Consulte §10.2.12 para obtener más información sobre las conversiones implícitas que implican parámetros de tipo.
Las conversiones de referencia implícitas son esas conversiones entre reference_types que se pueden demostrar que siempre se realizan correctamente y, por lo tanto, no requieren comprobaciones en tiempo de ejecución.
Las conversiones de referencia, implícitas o explícitas, nunca cambian la identidad referencial del objeto que se va a convertir.
Nota: En otras palabras, mientras que una conversión de referencia puede cambiar el tipo de la referencia, nunca cambia el tipo o valor del objeto al que se hace referencia. nota final
10.2.9 Conversiones de boxeo
Una conversión boxing permite convertir implícitamente un value_type en un reference_type. Existen las siguientes conversiones de conversión boxing:
- De cualquier value_type al tipo
object
. - De cualquier value_type al tipo
System.ValueType
. - Desde cualquier enum_type al tipo
System.Enum
. - Desde cualquier non_nullable_value_type a cualquier interface_type implementada por el non_nullable_value_type.
- Desde cualquier non_nullable_value_type a cualquier interface_type
I
de modo que haya una conversión boxing de la non_nullable_value_type a otra interface_typeI₀
yI₀
tenga una conversión de identidad aI
. - De cualquier non_nullable_value_type a cualquier interface_type
I
de modo que haya una conversión boxing de la non_nullable_value_type a otra interface_typeI₀
, yI₀
es convertible de varianza (§18.2.3.3) aI
. - Desde cualquier nullable_value_type a cualquier reference_type donde haya una conversión boxing del tipo subyacente del nullable_value_type al reference_type.
- A partir de un parámetro de tipo que no se sabe que es un tipo de referencia a ningún tipo, de modo que la conversión se permita mediante §10.2.12.
La conversión boxing de un valor que no acepta valores NULL consiste en asignar una instancia de objeto y copiar el valor en esa instancia.
La conversión boxing de un valor de un nullable_value_type genera una referencia nula si es el valor null (HasValue
es false) o el resultado de desencapsular y boxear el valor subyacente de lo contrario.
Nota: El proceso de conversión boxing se puede imaginar en términos de la existencia de una clase boxing para cada tipo de valor. Por ejemplo, considere la posibilidad de implementar una
struct S
interfazI
, con una clase boxing denominadaS_Boxing
.interface I { void M(); } struct S : I { public void M() { ... } } sealed class S_Boxing : I { S value; public S_Boxing(S value) { this.value = value; } public void M() { value.M(); } }
La conversión boxing de un valor
v
de tipoS
consiste ahora en ejecutar la expresiónnew S_Boxing(v)
y devolver la instancia resultante como un valor del tipo de destino de la conversión. Por lo tanto, las instruccionesS s = new S(); object box = s;
puede considerarse similar a:
S s = new S(); object box = new S_Boxing(s);
El tipo de conversión boxing imaginado descrito anteriormente no existe realmente. En su lugar, un valor boxed de tipo
S
tiene el tipoS
en tiempo de ejecución y una comprobación de tipo en tiempo de ejecución mediante elis
operador con un tipo de valor como el operando derecho comprueba si el operando izquierdo es una versión boxed del operando derecho. Por ejemplo,int i = 123; object box = i; if (box is int) { Console.Write("Box contains an int"); }
generará lo siguiente:
Box contains an int
Una conversión boxing implica realizar una copia del valor que se va a boxear. Esto es diferente de una conversión de un reference_type al tipo
object
, en el que el valor sigue haciendo referencia a la misma instancia y simplemente se considera como el tipoobject
menos derivado . Por ejemplo, lo siguiente:struct Point { public int x, y; public Point(int x, int y) { this.x = x; this.y = y; } } class A { void M() { Point p = new Point(10, 10); object box = p; p.x = 20; Console.Write(((Point)box).x); } }
generará el valor 10 en la consola porque la operación de conversión boxing implícita que se produce en la asignación de
p
parabox
hacer que se copie el valor dep
. Se habíaPoint
declarado enclass
su lugar, el valor 20 sería de salida porquep
ybox
haría referencia a la misma instancia.La analogía de una clase boxing no debe usarse como más que una herramienta útil para imaginar cómo funciona el boxeo conceptualmente. Hay numerosas diferencias sutiles entre el comportamiento descrito por esta especificación y el comportamiento que daría lugar a la implementación boxing de esta manera precisamente.
nota final
10.2.10 Conversiones dinámicas implícitas
Existe una conversión dinámica implícita de una expresión de tipo dinámica a cualquier tipo T
. La conversión se enlaza dinámicamente §12.3.3, lo que significa que se buscará una conversión implícita en tiempo de ejecución desde el tipo en tiempo de ejecución de la expresión a T
. Si no se encuentra ninguna conversión, se produce una excepción en tiempo de ejecución.
Esta conversión implícita aparentemente infringe los consejos a principios de §10.2 que una conversión implícita nunca debe provocar una excepción. Sin embargo, no es la propia conversión, sino la búsqueda de la conversión que provoca la excepción. El riesgo de excepciones en tiempo de ejecución es inherente al uso del enlace dinámico. Si no se desea el enlace dinámico de la conversión, la expresión se puede convertir primero en object
y, a continuación, al tipo deseado.
Ejemplo: a continuación se muestran conversiones dinámicas implícitas:
object o = "object"; dynamic d = "dynamic"; string s1 = o; // Fails at compile-time – no conversion exists string s2 = d; // Compiles and succeeds at run-time int i = d; // Compiles but fails at run-time – no conversion exists
Las asignaciones a
s2
yi
ambas emplean conversiones dinámicas implícitas, donde el enlace de las operaciones se suspende hasta el tiempo de ejecución. En tiempo de ejecución, se buscan conversiones implícitas del tipo en tiempo de ejecución de (string
) al tipo ded
destino. Se encuentra una conversión enstring
, pero no enint
.ejemplo final
10.2.11 Conversiones de expresiones constantes implícitas
Una conversión de expresión constante implícita permite las conversiones siguientes:
- Un constant_expression (§12.23) de tipo
int
se puede convertir al tiposbyte
, ,byte
short
,ushort
,uint
oulong
, siempre que el valor del constant_expression esté dentro del intervalo del tipo de destino. - Un constant_expression de tipo
long
se puede convertir en el tipoulong
, siempre que el valor del constant_expression no sea negativo.
10.2.12 Conversiones implícitas que implican parámetros de tipo
Para un type_parameter T
que se sabe que es un tipo de referencia (§15.2.5), existen las siguientes conversiones de referencia implícitas (§10.2.8):
- De
T
a su claseC
base efectiva , deT
a cualquier clase base deC
y deT
a cualquier interfaz implementada porC
. - De
T
a un interface_typeI
de laT
interfaz efectiva establecida y deT
a cualquier interfaz base deI
. - De
T
a un parámetroU
de tipo proporcionado queT
depende deU
(§15.2.5).Nota: Dado
T
que se sabe que es un tipo de referencia, dentro del ámbito deT
, el tipo en tiempo de ejecución deU
siempre será un tipo de referencia, aunqueU
no se sepa que es un tipo de referencia en tiempo de compilación. nota final - Desde el literal nulo (§6.4.5.7) a T.
Para una type_parameter T
que no se sabe que es un tipo de referencia §15.2.5, las siguientes conversiones que implican T
se consideran conversiones de conversión boxing (§10.2.9) en tiempo de compilación. En tiempo de ejecución, si T
es un tipo de valor, la conversión se ejecuta como una conversión boxing. En tiempo de ejecución, si T
es un tipo de referencia, la conversión se ejecuta como conversión de referencia implícita o conversión de identidad.
- De
T
a su claseC
base efectiva , deT
a cualquier clase base deC
y deT
a cualquier interfaz implementada porC
.Nota:
C
será uno de los tiposSystem.Object
,System.ValueType
oSystem.Enum
(de lo contrarioT
, se sabe que es un tipo de referencia). nota final - De
T
a un interface_typeI
de laT
interfaz efectiva establecida y deT
a cualquier interfaz base deI
.
Para un type_parameter T
que no se sabe que es un tipo de referencia, hay una conversión implícita de T
a un parámetro U
de tipo proporcionado T
depende de U
. En tiempo de ejecución, si T
es un tipo de valor y U
es un tipo de referencia, la conversión se ejecuta como una conversión boxing. En tiempo de ejecución, si ambos T
y U
son tipos de valor, T
y U
son necesariamente el mismo tipo y no se realiza ninguna conversión. En tiempo de ejecución, si T
es un tipo de referencia, es U
necesariamente también un tipo de referencia y la conversión se ejecuta como conversión de referencia implícita o conversión de identidad (§15.2.5).
Existen otras conversiones implícitas para un parámetro T
de tipo determinado:
- De a un tipo de
T
referencia si tiene una conversión implícita a un tipoS₀
de referencia yS₀
tiene una conversión de identidad aS
.S
En tiempo de ejecución, la conversión se ejecuta de la misma manera que la conversión aS₀
. - De a un tipo de
T
interfaz si tiene una conversión implícita a un tipoI₀
de interfaz yI₀
es variable de varianza aI
(§18.2.3.3).I
En tiempo de ejecución, siT
es un tipo de valor, la conversión se ejecuta como una conversión boxing. De lo contrario, la conversión se ejecuta como una conversión de referencia implícita o conversión de identidad.
En todos los casos, las reglas garantizan que una conversión se ejecute como una conversión boxing si y solo si en tiempo de ejecución la conversión procede de un tipo de valor a un tipo de referencia.
10.2.13 Conversiones implícitas de tupla
Existe una conversión implícita de una expresión E
de tupla a un tipo T
de tupla si E
tiene la misma aridad que T
y existe una conversión implícita de cada elemento en E
al tipo de elemento correspondiente en T
. La conversión se realiza mediante la creación de una instancia del T
tipo correspondiente System.ValueTuple<...>
y la inicialización de cada uno de sus campos en orden de izquierda a derecha mediante la evaluación de la expresión del elemento de tupla correspondiente de E
, convirtiéndola en el tipo de elemento correspondiente de T
utilizando la conversión implícita encontrada e inicializando el campo con el resultado.
Si un nombre de elemento de la expresión de tupla no coincide con un nombre de elemento correspondiente en el tipo de tupla, se emitirá una advertencia.
Ejemplo:
(int, string) t1 = (1, "One"); (byte, string) t2 = (2, null); (int, string) t3 = (null, null); // Error: No conversion (int i, string s) t4 = (i: 4, "Four"); (int i, string) t5 = (x: 5, s: "Five"); // Warning: Names are ignored
Las declaraciones de
t1
,t2
t4
yt5
son válidas, ya que existen conversiones implícitas de las expresiones de elemento a los tipos de elemento correspondientes. La declaración det3
no es válida, porque no hay ninguna conversión denull
aint
. La declaración det5
provoca una advertencia porque los nombres de elemento de la expresión de tupla difieren de los del tipo de tupla.ejemplo final
10.2.14 Conversiones implícitas definidas por el usuario
Una conversión implícita definida por el usuario consta de una conversión implícita estándar opcional, seguida de la ejecución de un operador de conversión implícita definido por el usuario, seguido de otra conversión implícita estándar opcional. Las reglas exactas para evaluar las conversiones implícitas definidas por el usuario se describen en §10.5.4.
10.2.15 Conversiones de funciones anónimas y conversiones de grupo de métodos
Las funciones anónimas y los grupos de métodos no tienen tipos en y de sí mismos, pero se pueden convertir implícitamente en tipos delegados. Además, algunas expresiones lambda se pueden convertir implícitamente en tipos de árbol de expresiones. Las conversiones de funciones anónimas se describen con más detalle en §10.7 y conversiones de grupos de métodos en §10.8.
10.2.16 Conversiones literales predeterminadas
Existe una conversión implícita de un default_literal (§12.8.21) a cualquier tipo. Esta conversión genera el valor predeterminado (§9.3) del tipo inferido.
10.2.17 Conversiones implícitas de lanzamiento
Aunque las expresiones throw no tienen un tipo, pueden convertirse implícitamente en cualquier tipo.
10.3 Conversiones explícitas
10.3.1 General
Las conversiones siguientes se clasifican como conversiones explícitas:
- Todas las conversiones implícitas (§10.2)
- Conversiones numéricas explícitas (§10.3.2)
- Conversiones de enumeración explícitas (§10.3.3)
- Conversiones explícitas que aceptan valores NULL (§10.3.4)
- Conversiones de tupla explícitas (§10.3.6)
- Conversiones de referencia explícitas (§10.3.5)
- Conversiones de interfaz explícitas
- Conversiones de unboxing (§10.3.7)
- Conversiones explícitas de parámetros de tipo (§10.3.8)
- Conversiones explícitas definidas por el usuario (§10.3.9)
Las conversiones explícitas pueden producirse en expresiones de conversión (§12.9.7).
El conjunto de conversiones explícitas incluye todas las conversiones implícitas.
Nota: Por ejemplo, permite usar una conversión explícita cuando existe una conversión de identidad implícita para forzar la selección de una sobrecarga de método determinada. nota final
Las conversiones explícitas que no son conversiones implícitas son conversiones que no se pueden demostrar siempre que se realizan correctamente, conversiones que se sabe que pueden perder información y conversiones entre dominios de tipos lo suficientemente diferentes para merecer la notación explícita.
10.3.2 Conversiones numéricas explícitas
Las conversiones numéricas explícitas son las conversiones de un numeric_type a otra numeric_type para las que aún no existe una conversión numérica implícita (§10.2.3):
- De
sbyte
abyte
,ushort
,uint
,ulong
ochar
. - De
byte
asbyte
ochar
. - De
short
asbyte
,byte
,ushort
,uint
,ulong
ochar
. - De
ushort
asbyte
,byte
,short
ochar
. - De
int
asbyte
, ,short
byte
,ushort
,uint
,ulong
ochar
. - De
uint
asbyte
,byte
,short
,ushort
,int
ochar
. - De
long
asbyte
,byte
, ,ushort
short
,int
,uint
, ,ulong
ochar
. - De
ulong
asbyte
,byte
, ,ushort
short
,int
,uint
, ,long
ochar
. - De
char
asbyte
,byte
oshort
. - De
float
asbyte
, ,byte
,ushort
short
,int
,uint
char
long
ulong
o .decimal
- De
double
asbyte
, ,byte
,ushort
short
,int
,uint
,long
,ulong
,char
,float
odecimal
. - De
decimal
asbyte
, ,byte
,ushort
short
,int
,uint
,long
,ulong
,char
,float
odouble
.
Dado que las conversiones explícitas incluyen todas las conversiones numéricas implícitas y explícitas, siempre es posible convertir de cualquier numeric_type a cualquier otra numeric_type mediante una expresión de conversión (§12.9.7).
Las conversiones numéricas explícitas posiblemente pierden información o, posiblemente, hacen que se produzcan excepciones. Una conversión numérica explícita se procesa de la siguiente manera:
- Para una conversión de un tipo entero a otro tipo entero, el procesamiento depende del contexto de comprobación de desbordamiento (§12.8.20) en el que tiene lugar la conversión:
- En un
checked
contexto, la conversión se realiza correctamente si el valor del operando de origen está dentro del intervalo del tipo de destino, pero produce unSystem.OverflowException
si el valor del operando de origen está fuera del intervalo del tipo de destino. - En un
unchecked
contexto, la conversión siempre se realiza correctamente y continúa de la siguiente manera.- Si el tipo de origen es mayor que el tipo de destino, el valor de origen se trunca descartando sus bits más significativos. El resultado se trata como un valor del tipo de destino.
- Si el tipo de origen tiene el mismo tamaño que el tipo de destino, el valor de origen se trata como un valor del tipo de destino.
- En un
- Para una conversión de
decimal
a un tipo entero, el valor de origen se redondea hacia cero hasta el valor entero más cercano y este valor entero se convierte en el resultado de la conversión. Si el valor entero resultante está fuera del intervalo del tipo de destino, se produce unaSystem.OverflowException
excepción . - Para una conversión de
float
odouble
a un tipo entero, el procesamiento depende del contexto de comprobación de desbordamiento (§12.8.20) en el que tiene lugar la conversión:- En un contexto comprobado, la conversión continúa de la siguiente manera:
- Si el valor del operando es NaN o infinito, se produce una
System.OverflowException
excepción . - De lo contrario, el operando de origen se redondea hacia cero hasta el valor entero más cercano. Si este valor entero está dentro del intervalo del tipo de destino, este valor es el resultado de la conversión.
- De lo contrario, se produce una excepción
System.OverflowException
.
- Si el valor del operando es NaN o infinito, se produce una
- En un contexto sin marcar, la conversión siempre se realiza correctamente y continúa como se indica a continuación.
- Si el valor del operando es NaN o infinito, el resultado de la conversión es un valor no especificado del tipo de destino.
- De lo contrario, el operando de origen se redondea hacia cero hasta el valor entero más cercano. Si este valor entero está dentro del intervalo del tipo de destino, este valor es el resultado de la conversión.
- De lo contrario, el resultado de la conversión es un valor no especificado del tipo de destino.
- En un contexto comprobado, la conversión continúa de la siguiente manera:
- Para una conversión de
double
afloat
, eldouble
valor se redondea al valor más cercanofloat
. Si eldouble
valor es demasiado pequeño para representar comofloat
, el resultado se convierte en cero con el mismo signo que el valor. Si la magnitud deldouble
valor es demasiado grande para representar comofloat
, el resultado se convierte en infinito con el mismo signo que el valor. Si eldouble
valor es NaN, el resultado también es NaN. - Para una conversión de
float
odouble
adecimal
, el valor de origen se convierte endecimal
representación y se redondea al número más cercano si es necesario (§8.3.8).- Si el valor de origen es demasiado pequeño para representar como
decimal
, el resultado se convierte en cero, conservando el signo del valor original sidecimal
admite valores cero firmados. - Si la magnitud del valor de origen es demasiado grande para representar como ,
decimal
o ese valor es infinito, el resultado es infinito conservando el signo del valor original, si la representación decimal admite infinities; de lo contrario, se produce una excepción System.OverflowException. - Si el valor de origen es NaN, el resultado es NaN si la representación decimal admite NaNs; De lo contrario, se produce una excepción System.OverflowException.
- Si el valor de origen es demasiado pequeño para representar como
- Para una conversión de a
float
decimal
odouble
, eldecimal
valor se redondea al valor más cercanodouble
ofloat
. Si la magnitud del valor de origen es demasiado grande para representar en el tipo de destino o ese valor es infinito, el resultado es infinito conservando el signo del valor original. Si el valor de origen es NaN, el resultado es NaN. Aunque esta conversión puede perder precisión, nunca hace que se produzca una excepción.
Nota: El
decimal
tipo no es necesario para admitir las infinities o los valores NaN, pero puede hacerlo; su intervalo puede ser menor que el intervalo defloat
ydouble
, pero no se garantiza que sea. Paradecimal
representaciones sin infinities o valores NaN, y con un intervalo menor quefloat
, el resultado de una conversión dedecimal
a ofloat
double
nunca será infinito o NaN. nota final
10.3.3 Conversiones de enumeración explícitas
Las conversiones de enumeración explícitas son:
- De
sbyte
,byte
,short
, ,int
ushort
,uint
,long
ulong
char
float
, odecimal
double
a cualquier enum_type. - De cualquier enum_type a
sbyte
,byte
,uint
long
int
ushort
char
short
ulong
,float
, ,double
o .decimal
- Desde cualquier enum_type a cualquier otro enum_type.
Una conversión de enumeración explícita entre dos tipos se procesa tratando cualquier enum_type participante como el tipo subyacente de esa enum_type y, a continuación, realizando una conversión numérica implícita o explícita entre los tipos resultantes.
Ejemplo: Dado un enum_type
E
con y el tipo subyacente deint
, una conversión deE
abyte
se procesa como una conversión numérica explícita (§10.3.2) deint
a y una conversión debyte
abyte
E
se procesa como una conversión numérica implícita (§10.2.3) debyte
aint
. ejemplo final
10.3.4 Conversiones explícitas que aceptan valores NULL
Las conversiones explícitas que aceptan valores NULL son las conversiones que aceptan valores NULL (§10.6.1) derivadas de conversiones predefinidas explícitas e implícitas.
10.3.5 Conversiones de referencia explícitas
Las conversiones de referencia explícitas son:
- De objeto a cualquier otro reference_type.
- De cualquier class_type
S
a cualquier class_typeT
, proporcionadoS
es una clase base deT
. - Desde cualquier class_type a cualquier interface_type
T
S
, siempreS
que no esté sellado y proporcionadoS
no implemente .T
- Desde cualquier interface_type
S
a cualquier class_typeT
, siempreT
que no esté sellado o proporcionadoT
implementeS
. - De cualquier interface_type
S
a cualquier interface_typeT
, proporcionadoS
no se deriva deT
. - De un array_type
S
con un tipoSᵢ
de elemento a un array_typeT
con un tipoTᵢ
de elemento , siempre que se cumplan todas las siguientes condiciones:S
yT
solo difieren en el tipo de elemento. En otras palabras,S
yT
tienen el mismo número de dimensiones.- Existe una conversión de referencia explícita de
Sᵢ
aTᵢ
.
- Desde
System.Array
y las interfaces que implementa, en cualquier array_type. - Desde un array_type
S[]
unidimensional aSystem.Collections.Generic.IList<T>
,System.Collections.Generic.IReadOnlyList<T>
y sus interfaces base, siempre que haya una conversión de identidad o una conversión de referencia explícita deS
aT
. - Desde
System.Collections.Generic.IList<S>
,System.Collections.Generic.IReadOnlyList<S>
y sus interfaces base a un tipoT[]
de matriz unidimensional, siempre que haya una conversión de identidad o una conversión de referencia explícita deS
a T. - Desde
System.Delegate
y las interfaces que implementa en cualquier delegate_type. - Desde un tipo
S
de referencia a un tipoT
de referencia si tiene una conversión de referencia explícita deS
a un tipoT₀
de referencia yT₀
hay una conversión de identidad deT₀
aT
. - Desde un tipo
S
de referencia a una interfaz o un tipoT
delegado si hay una conversión de referencia explícita deS
a una interfaz o un tipoT₀
delegado y esT₀
variable de varianza aT
§T
T₀
18.2.3.3. - De
D<S₁...Sᵥ>
a dondeD<X₁...Xᵥ>
D<T₁...Tᵥ>
es un tipo delegado genérico,D<S₁...Sᵥ>
no es compatible con o idéntico aD<T₁...Tᵥ>
, y para cada parámetroXᵢ
de tipo deD
las siguientes suspensiones:- Si
Xᵢ
es invariable, entoncesSᵢ
es idéntico aTᵢ
. - Si
Xᵢ
es covariante, hay una conversión de identidad, conversión de referencia implícita o conversión de referencia explícita deSᵢ
aTᵢ
. - Si
Xᵢ
es contravariante,Sᵢ
yTᵢ
son idénticos o ambos tipos de referencia.
- Si
- Conversiones explícitas que implican parámetros de tipo que se sabe que son tipos de referencia. Para obtener más información sobre las conversiones explícitas que implican parámetros de tipo, consulte §10.3.8.
Las conversiones de referencia explícitas son esas conversiones entre reference_types que requieren comprobaciones en tiempo de ejecución para asegurarse de que son correctas.
Para que una conversión de referencia explícita se realice correctamente en tiempo de ejecución, el valor del operando de origen será null
o el tipo del objeto al que hace referencia el operando de origen será un tipo que se pueda convertir al tipo de destino mediante una conversión de referencia implícita (§10.2.8). Si se produce un error en una conversión de referencia explícita, se produce una System.InvalidCastException
excepción .
Nota: Las conversiones de referencia, implícitas o explícitas, nunca cambian el valor de la propia referencia (§8.2.1), solo su tipo; tampoco cambia el tipo o el valor del objeto al que se hace referencia. nota final
10.3.6 Conversiones explícitas de tupla
Existe una conversión explícita desde una expresión E
de tupla a un tipo T
de tupla si E
tiene la misma aridad que T
y existe una conversión implícita o explícita de cada elemento en E
al tipo de elemento correspondiente en T
. La conversión se realiza mediante la creación de una instancia del T
tipo correspondiente System.ValueTuple<...>
y la inicialización de cada uno de sus campos en orden de izquierda a derecha mediante la evaluación de la expresión del elemento de tupla correspondiente de E
, convirtiéndola en el tipo de elemento correspondiente de T
utilizando la conversión explícita encontrada e inicializando el campo con el resultado.
10.3.7 Conversiones de unboxing
Una conversión de unboxing permite convertir explícitamente un reference_type en un value_type. Existen las siguientes conversiones de unboxing:
- Desde el tipo
object
hasta cualquier value_type. - Desde el tipo
System.ValueType
hasta cualquier value_type. - Desde el tipo
System.Enum
hasta cualquier enum_type. - Desde cualquier interface_type a cualquier non_nullable_value_type que implemente el interface_type.
- Desde cualquier interface_type a cualquier non_nullable_value_type
I
donde haya una conversión unboxing de un interface_type al tipo de non_nullable_valueI₀
y una conversión de identidad deI
a .I₀
- De cualquier interface_type
I
a cualquier non_nullable_value_type donde haya una conversión de unboxing de un interface_type a la non_nullable_value_typeI₀
y seaI₀
variance_convertible aI
oI
sea de varianza convertible aI₀
(§18.2.3.3). - Desde cualquier reference_type a cualquier nullable_value_type donde haya una conversión unboxing de reference_type a la non_nullable_value_type subyacente del nullable_value_type.
- A partir de un parámetro de tipo que no se sabe que es un tipo de valor a cualquier tipo, de modo que la conversión se permita mediante §10.3.8.
Una operación de unboxing en un non_nullable_value_type consiste en comprobar primero que la instancia de objeto es un valor con conversión boxing del non_nullable_value_type especificado y, a continuación, copiar el valor fuera de la instancia.
La unboxing a un nullable_value_type genera el valor NULL del nullable_value_type si el operando de origen es null
o el resultado ajustado de desencapsular la instancia del objeto en el tipo subyacente del nullable_value_type de lo contrario.
Nota: Referencia a la clase boxing imaginaria descrita en §10.2.9, una conversión unboxing de un cuadro de objeto a un value_type
S
consiste en ejecutar la expresión((S_Boxing)box).value
. Por lo tanto, las instruccionesobject box = new S(); S s = (S)box;
corresponde conceptualmente a
object box = new S_Boxing(new S()); S s = ((S_Boxing)box).value;
nota final
Para que una conversión unboxing a un non_nullable_value_type determinado se realice correctamente en tiempo de ejecución, el valor del operando de origen será una referencia a un valor con conversión boxing de ese non_nullable_value_type. Si el operando de origen es null
un System.NullReferenceException
se produce. Si el operando de origen es una referencia a un objeto incompatible, se produce una System.InvalidCastException
excepción .
Para que una conversión unboxing a un nullable_value_type determinado se realice correctamente en tiempo de ejecución, el valor del operando de origen será null o una referencia a un valor con conversión boxing del non_nullable_value_type subyacente del nullable_value_type. Si el operando de origen es una referencia a un objeto incompatible, se produce una System.InvalidCastException
excepción .
10.3.8 Conversiones explícitas que implican parámetros de tipo
Para un type_parameter T
que se sabe que es un tipo de referencia (§15.2.5), existen las siguientes conversiones de referencia explícitas (§10.3.5):
- De la clase
C
base efectiva deT
aT
y de cualquier clase base deC
aT
. - De cualquier interface_type a
T
. - De
T
a cualquier interface_typeI
proporcionado ya no hay una conversión de referencia implícita deT
aI
. - De un type_parameter
U
aT
siempre queT
dependaU
de (§15.2.5).Nota: Dado
T
que se sabe que es un tipo de referencia, dentro del ámbito deT
, el tipo en tiempo de ejecución de siempre será un tipo de referencia, aunqueU
no se sepa que es un tipo de referencia en tiempo de compilación. nota final
Para un type_parameter T
que no se sabe que es un tipo de referencia (§15.2.5), las siguientes conversiones que implican T
se consideran conversiones de unboxing (§10.3.7) en tiempo de compilación. En tiempo de ejecución, si T
es un tipo de valor, la conversión se ejecuta como una conversión de unboxing. En tiempo de ejecución, si T
es un tipo de referencia, la conversión se ejecuta como una conversión de referencia explícita o conversión de identidad.
- De la clase
C
base efectiva deT
aT
y de cualquier clase base deC
aT
.Nota: C será uno de los tipos
System.Object
,System.ValueType
oSystem.Enum
(de lo contrarioT
, se sabe que es un tipo de referencia). nota final - De cualquier interface_type a
T
.
Para un type_parameter T
que no se sabe que es un tipo de referencia (§15.2.5), existen las siguientes conversiones explícitas:
- De
T
a cualquier interface_typeI
siempre que no haya una conversión implícita deT
aI
. Esta conversión consta de una conversión boxing implícita (§10.2.9) de aobject
seguida deT
una conversión de referencia explícita deobject
aI
. En tiempo de ejecución, siT
es un tipo de valor, la conversión se ejecuta como una conversión boxing seguida de una conversión de referencia explícita. En tiempo de ejecución, siT
es un tipo de referencia, la conversión se ejecuta como una conversión de referencia explícita. - De un parámetro
U
de tipo aT
proporcionado queT
depende deU
(§15.2.5). En tiempo de ejecución, siT
es un tipo de valor yU
es un tipo de referencia, la conversión se ejecuta como una conversión de unboxing. En tiempo de ejecución, si ambosT
yU
son tipos de valor,T
yU
son necesariamente el mismo tipo y no se realiza ninguna conversión. En tiempo de ejecución, siT
es un tipo de referencia, entoncesU
también es necesariamente un tipo de referencia y la conversión se ejecuta como una conversión de referencia explícita o conversión de identidad.
En todos los casos, las reglas garantizan que una conversión se ejecute como una conversión unboxing si y solo si en tiempo de ejecución la conversión procede de un tipo de referencia a un tipo de valor.
Las reglas anteriores no permiten una conversión explícita directa de un parámetro de tipo sin restricciones a un tipo que no sea de interfaz, lo que puede ser sorprendente. La razón de esta regla es evitar confusiones y aclarar la semántica de estas conversiones.
Ejemplo: Considere la siguiente declaración:
class X<T> { public static long F(T t) { return (long)t; // Error } }
Si se permitía la conversión explícita directa de
t
along
, podría esperarse fácilmente queX<int>.F(7)
devolvería7L
. Sin embargo, no lo haría, ya que las conversiones numéricas estándar solo se consideran cuando se sabe que los tipos son numéricos en tiempo de enlace. Para que la semántica sea clara, el ejemplo anterior debe escribirse en su lugar:class X<T> { public static long F(T t) { return (long)(object)t; // Ok, but will only work when T is long } }
Este código ahora se compilará, pero la ejecución
X<int>.F(7)
produciría una excepción en tiempo de ejecución, ya que un cuadroint
no se puede convertir directamente en .long
ejemplo final
10.3.9 Conversiones explícitas definidas por el usuario
Una conversión explícita definida por el usuario consta de una conversión explícita estándar opcional, seguida de la ejecución de un operador de conversión implícito o explícito definido por el usuario, seguido de otra conversión explícita estándar opcional. Las reglas exactas para evaluar conversiones explícitas definidas por el usuario se describen en §10.5.5.
10.4 Conversiones estándar
10.4.1 General
Las conversiones estándar son aquellas conversiones predefinidas que pueden producirse como parte de una conversión definida por el usuario.
10.4.2 Conversiones implícitas estándar
Las conversiones implícitas siguientes se clasifican como conversiones implícitas estándar:
- Conversiones de identidad (§10.2.2)
- Conversiones numéricas implícitas (§10.2.3)
- Conversiones implícitas que aceptan valores NULL (§10.2.6)
- Conversiones de literales NULL (§10.2.7)
- Conversiones de referencia implícitas (§10.2.8)
- Conversiones de boxeo (§10.2.9)
- Conversiones implícitas de expresiones constantes (§10.2.11)
- Conversiones implícitas que implican parámetros de tipo (§10.2.12)
Las conversiones implícitas estándar excluyen específicamente las conversiones implícitas definidas por el usuario.
10.4.3 Conversiones explícitas estándar
Las conversiones explícitas estándar son todas las conversiones implícitas estándar más el subconjunto de las conversiones explícitas para las que existe una conversión implícita estándar opuesta.
Nota: En otras palabras, si existe una conversión implícita estándar de un tipo
A
a un tipoB
, existe una conversión explícita estándar de tipoA
a tipoB
y de tipoB
a tipoA
. nota final
10.5 Conversiones definidas por el usuario
10.5.1 General
C# permite aumentar las conversiones implícitas y explícitas predefinidas por las conversiones definidas por el usuario. Las conversiones definidas por el usuario se introducen declarando operadores de conversión (§15.10.4) en tipos de clase y estructura.
10.5.2 Conversiones definidas por el usuario permitidas
C# solo permite declarar determinadas conversiones definidas por el usuario. En concreto, no es posible volver a definir una conversión implícita o explícita ya existente.
Para un tipo de origen y un tipo T
S
de destino determinado , si S
o T
son tipos de valor que aceptan valores NULL, deje S₀
y T₀
haga referencia a sus tipos subyacentes; de lo contrarioS₀
, y T₀
son iguales a S
y T
respectivamente. Una clase o estructura puede declarar una conversión de un tipo de origen a un tipo S
T
de destino solo si se cumplen todas las siguientes condiciones:
S₀
yT₀
son tipos diferentes.T₀
OS₀
es el tipo de clase o estructura en el que tiene lugar la declaración del operador.- Ni ni
S₀
T₀
es un interface_type. - Excluyendo las conversiones definidas por el usuario, una conversión no existe de
S
aT
o deT
aS
.
Las restricciones que se aplican a las conversiones definidas por el usuario se especifican en §15.10.4.
10.5.3 Evaluación de conversiones definidas por el usuario
Una conversión definida por el usuario convierte una expresión de origen, que puede tener un tipo de origen, a otro tipo, denominado tipo de destino. La evaluación de una conversión definida por el usuario se centra en encontrar el operador de conversión definido por el usuario más específico para la expresión de origen y el tipo de destino. Esta determinación se divide en varios pasos:
- Buscar el conjunto de clases y estructuras desde las que se considerarán los operadores de conversión definidos por el usuario. Este conjunto consta del tipo de origen y sus clases base, si el tipo de origen existe, junto con el tipo de destino y sus clases base. Para ello, se supone que solo las clases y estructuras pueden declarar operadores definidos por el usuario y que los tipos que no son de clase no tienen clases base. Además, si el tipo de origen o de destino es un tipo de valor que acepta valores NULL, su tipo subyacente se usa en su lugar.
- A partir de ese conjunto de tipos, determinar qué operadores de conversión definidos por el usuario y elevado son aplicables. Para que un operador de conversión sea aplicable, será posible realizar una conversión estándar (§10.4) desde la expresión de origen al tipo de operando del operador y será posible realizar una conversión estándar del tipo de resultado del operador al tipo de destino.
- A partir del conjunto de operadores definidos por el usuario aplicables, determinar qué operador es inequívocamente el más específico. En términos generales, el operador más específico es el operador cuyo tipo de operando es "más cercano" a la expresión de origen y cuyo tipo de resultado es "más cercano" al tipo de destino. Los operadores de conversión definidos por el usuario se prefieren sobre los operadores de conversión elevado. Las reglas exactas para establecer el operador de conversión definido por el usuario más específico se definen en las subclases siguientes.
Una vez identificado un operador de conversión definido por el usuario más específico, la ejecución real de la conversión definida por el usuario implica hasta tres pasos:
- En primer lugar, si es necesario, realizar una conversión estándar de la expresión de origen al tipo de operando del operador de conversión definido por el usuario o elevado.
- A continuación, invocar al operador de conversión definido por el usuario o elevado para realizar la conversión.
- Por último, si es necesario, realizar una conversión estándar del tipo de resultado del operador de conversión definido por el usuario al tipo de destino.
La evaluación de una conversión definida por el usuario nunca implica más de un operador de conversión definido por el usuario o elevado. En otras palabras, una conversión de tipo S
a tipo T
nunca ejecutará primero una conversión definida por el usuario desde S
a X
y, a continuación, ejecutará una conversión definida por el usuario desde X
a T
.
- Las definiciones exactas de evaluación de conversiones implícitas o explícitas definidas por el usuario se proporcionan en las subclases siguientes. Las definiciones hacen uso de los siguientes términos:
- Si existe una conversión implícita estándar (§10.4.2) de un tipo
A
a un tipoB
y, si niA
B
ni son interface_types
,A
se dice que está abarcado porB
yB
se dice que abarca .A
- Si existe una conversión implícita estándar (§10.4.2) desde una expresión
E
a un tipoB
, y si niB
ni el tipo deE
(si tiene uno) son interface_types
,E
se dice que está abarcado porB
yB
se dice que abarcaE
. - El tipo más abarcante de un conjunto de tipos es el que abarca todos los demás tipos del conjunto. Si ningún tipo único abarca todos los demás tipos, el conjunto no tiene ningún tipo que abarque más. En términos más intuitivos, el tipo más abarcativo es el tipo "más grande" del conjunto, el tipo en el que se puede convertir implícitamente cada uno de los otros tipos.
- El tipo más abarcado de un conjunto de tipos es el que abarca todos los demás tipos del conjunto. Si no hay ningún tipo único incluido en todos los demás tipos, el conjunto no tiene ningún tipo más abarcado. En términos más intuitivos, el tipo más abarcado es el tipo "más pequeño" del conjunto, el único tipo que se puede convertir implícitamente en cada uno de los otros tipos.
10.5.4 Conversiones implícitas definidas por el usuario
Una conversión implícita definida por el usuario de una expresión E
a un tipo T
se procesa de la siguiente manera:
Determine los tipos
S
yS₀
T₀
.- Si
E
tiene un tipo, vamosS
a ser ese tipo. - Si
S
oT
son tipos de valor que aceptan valores NULL, letSᵢ
yTᵢ
sean sus tipos subyacentes, de lo contrario, letSᵢ
y beS
yTᵢ
T
, respectivamente. - Si
Sᵢ
oTᵢ
son parámetros de tipo, letS₀
yT₀
sean sus clases base efectivas, de lo contrario, let yS₀
beSₓ
yT₀
Tᵢ
, respectivamente.
- Si
Busque el conjunto de tipos, ,
D
desde el que se considerarán los operadores de conversión definidos por el usuario. Este conjunto consta deS₀
(siS₀
existe y es una clase o estructura), las clases base deS₀
(siS₀
existe y es una clase) yT₀
(siT₀
es una clase o estructura). Un tipo se agrega al conjuntoD
solo si no existe una conversión de identidad a otro tipo ya incluido en el conjunto.Busque el conjunto de operadores de conversión definidos por el usuario y eliminados aplicables,
U
. Este conjunto consta de los operadores de conversión implícitos definidos por el usuario declarados por las clases o estructuras deD
que convierten de un tipo que abarcaE
a un tipo incluido enT
. SiU
está vacío, la conversión no está definida y se produce un error en tiempo de compilación.- Si
S
existe y cualquiera de los operadores deU
conversión deS
,Sₓ
esS
. - De lo contrario,
Sₓ
es el tipo más abarcado del conjunto combinado de tipos de origen de los operadores deU
. Si no se encuentra exactamente un tipo más abarcado, la conversión es ambigua y se produce un error en tiempo de compilación.
- Si
Busque el tipo de destino más específico, ,
Tₓ
de los operadores enU
:- Si alguno de los operadores de conversión en
U
T
,Tₓ
esT
. - De lo contrario,
Tₓ
es el tipo más abarcante del conjunto combinado de tipos de destino de los operadores deU
. Si no se encuentra exactamente un tipo más abarcador, la conversión es ambigua y se produce un error en tiempo de compilación.
- Si alguno de los operadores de conversión en
Busque el operador de conversión más específico:
- Si
U
contiene exactamente un operador de conversión definido por el usuario que convierte deSₓ
aTₓ
, este es el operador de conversión más específico. - De lo contrario, si
U
contiene exactamente un operador de conversión elevado que convierte deSₓ
aTₓ
, este es el operador de conversión más específico. - De lo contrario, la conversión es ambigua y se produce un error en tiempo de compilación.
- Si
Por último, aplique la conversión:
- Si E aún no tiene el tipo
Sₓ
, se realiza una conversión implícita estándar deE
aSₓ
. - El operador de conversión más específico se invoca para convertir de
Sₓ
aTₓ
. - Si
Tₓ
noT
es , se realiza una conversión implícita estándar deTₓ
aT
.
- Si E aún no tiene el tipo
Existe una conversión implícita definida por el usuario de un tipo S
a un tipo T
si existe una conversión implícita definida por el usuario desde una variable de tipo S
a T
.
10.5.5 Conversiones explícitas definidas por el usuario
Una conversión explícita definida por el usuario de una expresión E
a un tipo T
se procesa de la siguiente manera:
- Determine los tipos
S
yS₀
T₀
.- Si
E
tiene un tipo, vamosS
a ser ese tipo. - Si
S
oT
son tipos de valor que aceptan valores NULL, letSᵢ
yTᵢ
sean sus tipos subyacentes, de lo contrario, letSᵢ
y beS
yTᵢ
T
, respectivamente. - Si
Sᵢ
oTᵢ
son parámetros de tipo, letS₀
yT₀
sean sus clases base efectivas, de lo contrario, let yS₀
beSᵢ
yT₀
Tᵢ
, respectivamente.
- Si
- Busque el conjunto de tipos, ,
D
desde el que se considerarán los operadores de conversión definidos por el usuario. Este conjunto consta deS₀
(siS₀
existe y es una clase o estructura), las clases base deS₀
(siS₀
existe y es una clase),T₀
(siT₀
es una clase o estructura) y las clases base deT₀
(siT₀
es una clase).A
el tipo se agrega al conjuntoD
solo si no existe una conversión de identidad a otro tipo incluido en el conjunto. - Busque el conjunto de operadores de conversión definidos por el usuario y eliminados aplicables,
U
. Este conjunto consta de los operadores de conversión implícitos o explícitos definidos por el usuario declarados por las clases o estructuras enD
que se convierten de un tipo que abarcaE
o abarca (S
si existe) a un tipo que abarca o abarca .T
SiU
está vacío, la conversión no está definida y se produce un error en tiempo de compilación. - Busque el tipo de origen más específico, ,
Sₓ
de los operadores enU
:- Si existe S y cualquiera de los operadores de
U
conversión deS
, esSₓ
S
. - De lo contrario, si alguno de los operadores de
U
conversión de tipos que abarcan , esSₓ
el tipo más abarcadoE
en el conjunto combinado de tipos de origen de esos operadores. Si no se encuentra ningún tipo más abarcado, la conversión es ambigua y se produce un error en tiempo de compilación. - De lo contrario,
Sₓ
es el tipo más abarcante del conjunto combinado de tipos de origen de los operadores deU
. Si no se encuentra exactamente un tipo más abarcador, la conversión es ambigua y se produce un error en tiempo de compilación.
- Si existe S y cualquiera de los operadores de
- Busque el tipo de destino más específico, ,
Tₓ
de los operadores enU
:- Si alguno de los operadores de conversión en
U
T
,Tₓ
esT
. - De lo contrario, si alguno de los operadores de
U
conversión a tipos que están incluidos enT
, esTₓ
el tipo más abarcante del conjunto combinado de tipos de destino de esos operadores. Si no se encuentra exactamente un tipo más abarcador, la conversión es ambigua y se produce un error en tiempo de compilación. - De lo contrario,
Tₓ
es el tipo más abarcado del conjunto combinado de tipos de destino de los operadores deU
. Si no se encuentra ningún tipo más abarcado, la conversión es ambigua y se produce un error en tiempo de compilación.
- Si alguno de los operadores de conversión en
- Busque el operador de conversión más específico:
- Si U contiene exactamente un operador de conversión definido por el usuario que convierte de
Sₓ
aTₓ
, este es el operador de conversión más específico. - De lo contrario, si
U
contiene exactamente un operador de conversión elevado que convierte deSₓ
aTₓ
, este es el operador de conversión más específico. - De lo contrario, la conversión es ambigua y se produce un error en tiempo de compilación.
- Si U contiene exactamente un operador de conversión definido por el usuario que convierte de
- Por último, aplique la conversión:
- Si
E
aún no tiene el tipoSₓ
, se realiza una conversión explícita estándar de E aSₓ
. - El operador de conversión definido por el usuario más específico se invoca para convertir de
Sₓ
aTₓ
. - Si
Tₓ
noT
es , se realiza una conversión explícita estándar deTₓ
aT
.
- Si
Existe una conversión explícita definida por el usuario de un tipo S
a un tipo T
si existe una conversión explícita definida por el usuario desde una variable de tipo S
a T
.
10.6 Conversiones que implican tipos que aceptan valores NULL
10.6.1 Conversiones que aceptan valores NULL
Las conversiones que aceptan valores NULL permiten conversiones predefinidas que operan en tipos de valor que no aceptan valores NULL para que también se usen con formas que aceptan valores NULL de esos tipos. Para cada una de las conversiones implícitas o explícitas predefinidas que convierten de un tipo de valor que no acepta valores NULL a un tipo S
T
de valor que no acepta valores NULL (§10.2.2, §10.2.4, §10.2.11, §10.3.2 y §10.3.3), existen las siguientes conversiones que aceptan valores NULL:
- Conversión implícita o explícita de
S?
aT?
- Conversión implícita o explícita de
S
aT?
- Conversión explícita de
S?
aT
.
Una conversión que acepta valores NULL se clasifica como una conversión implícita o explícita.
Algunas conversiones que aceptan valores NULL se clasifican como conversiones estándar y pueden producirse como parte de una conversión definida por el usuario. En concreto, todas las conversiones implícitas que aceptan valores NULL se clasifican como conversiones implícitas estándar (§10.4.2) y esas conversiones explícitas que cumplen los requisitos de §10.4.3 se clasifican como conversiones explícitas estándar.
Evaluación de una conversión que acepta valores NULL en función de una conversión subyacente de S
para T
continuar de la siguiente manera:
- Si la conversión que acepta valores NULL es de
S?
aT?
:- Si el valor de origen es null (
HasValue
la propiedad esfalse
), el resultado es el valor null de tipoT?
. - De lo contrario, la conversión se evalúa como una desencapsulación de
S?
aS
, seguida de la conversión subyacente deS
aT
, seguida de un ajuste deT
aT?
.
- Si el valor de origen es null (
- Si la conversión que acepta valores NULL es de
S
aT?
, la conversión se evalúa como la conversión subyacente deS
aT
seguida de un ajuste deT
aT?
. - Si la conversión que acepta valores NULL es de
S?
aT
, la conversión se evalúa como una desencapsulación deS?
aS
seguida de la conversión subyacente deS
aT
.
10.6.2 Conversiones levantadas
Dado un operador de conversión definido por el usuario que convierte de un tipo S
de valor que no acepta valores NULL a un tipo T
de valor que no acepta valores NULL, existe un operador de conversión elevado que convierte de S?
a T?
. Este operador de conversión elevado realiza una desencapsulación de S?
a S
seguida de la conversión definida por el usuario de a T
seguida de S
un ajuste de T
a T?
, excepto que un valor S?
NULO convierte directamente en un valor NULL con valores NULL.T?
Un operador de conversión elevado tiene la misma clasificación implícita o explícita que su operador de conversión definido por el usuario subyacente.
10.7 Conversiones de funciones anónimas
10.7.1 General
Un anonymous_method_expression o lambda_expression se clasifica como una función anónima (§12.19). La expresión no tiene un tipo, pero se puede convertir implícitamente en un tipo delegado compatible. Algunas expresiones lambda también se pueden convertir implícitamente en un tipo de árbol de expresión compatible.
En concreto, una función F
anónima es compatible con un tipo D
de delegado proporcionado:
- Si
F
contiene un anonymous_function_signature,D
yF
tienen el mismo número de parámetros. - Si
F
no contiene un anonymous_function_signature,D
puede tener cero o más parámetros de cualquier tipo, siempre y cuando ningún parámetro de sea un parámetro deD
salida. - Si
F
tiene una lista de parámetros con tipo explícito, cada parámetro deD
tiene los mismos modificadores que el parámetro correspondiente enF
y existe una conversión de identidad entre el parámetro correspondiente enF
. - Si
F
tiene una lista de parámetros con tipo implícito,D
no tiene parámetros de referencia ni de salida. - Si el cuerpo de
F
es una expresión yD
tiene un tipo de valor devuelto void oF
es asincrónico yD
tiene un«TaskType»
tipo de valor devuelto (§15.15.1), cuando cada parámetro deF
tiene el tipo del parámetro correspondiente enD
, el cuerpo deF
es una expresión válida (w.r.t §12) que se permitiría como un statement_expression (§13.7). - Si el cuerpo de
F
es un bloque yD
tiene un tipo de valor devuelto void oF
es asincrónico yD
tiene un«TaskType»
tipo de valor devuelto , cuando cada parámetro deF
recibe el tipo del parámetro correspondiente enD
, el cuerpo deF
es un bloque válido (w.r.t §13.3) en el que ningunareturn
instrucción especifica una expresión. - Si el cuerpo de
F
es una expresión y noF
es asincrónico yD
tiene unvoid
tipoT
de valor devuelto , oF
es asincrónico yD
tiene un«TaskType»<T>
tipo de valor devuelto (§15.15.1), cuando cada parámetro deF
tiene el tipo del parámetro correspondiente en , el cuerpo deF
es una expresión válida (w.r.t §12) que se puede convertir implícitamente enD
T
. - Si el cuerpo de
F
es un bloque y noF
es asincrónico yD
tiene un tipoT
de valor devuelto no nulo , oF
es asincrónico yD
tiene un«TaskType»<T>
tipo de valor devuelto, cuando cada parámetro deF
se asigna el tipo del parámetro correspondiente enD
, el cuerpo de es un bloque deF
instrucciones válido (w.r.t §13.3) con un punto final no accesible en el que cada instrucción de devolución especifica una expresión que se puede convertirT
implícitamente en .
Ejemplo: En los ejemplos siguientes se muestran estas reglas:
delegate void D(int x); D d1 = delegate { }; // Ok D d2 = delegate() { }; // Error, signature mismatch D d3 = delegate(long x) { }; // Error, signature mismatch D d4 = delegate(int x) { }; // Ok D d5 = delegate(int x) { return; }; // Ok D d6 = delegate(int x) { return x; }; // Error, return type mismatch delegate void E(out int x); E e1 = delegate { }; // Error, E has an output parameter E e2 = delegate(out int x) { x = 1; }; // Ok E e3 = delegate(ref int x) { x = 1; }; // Error, signature mismatch delegate int P(params int[] a); P p1 = delegate { }; // Error, end of block reachable P p2 = delegate { return; }; // Error, return type mismatch P p3 = delegate { return 1; }; // Ok P p4 = delegate { return "Hello"; }; // Error, return type mismatch P p5 = delegate(int[] a) // Ok { return a[0]; }; P p6 = delegate(params int[] a) // Error, params modifier { return a[0]; }; P p7 = delegate(int[] a) // Error, return type mismatch { if (a.Length > 0) return a[0]; return "Hello"; }; delegate object Q(params int[] a); Q q1 = delegate(int[] a) // Ok { if (a.Length > 0) return a[0]; return "Hello"; };
ejemplo final
Ejemplo: Los ejemplos siguientes usan un tipo
Func<A,R>
de delegado genérico que representa una función que toma un argumento de tipoA
y devuelve un valor de tipoR
:delegate R Func<A,R>(A arg);
En las asignaciones
Func<int,int> f1 = x => x + 1; // Ok Func<int,double> f2 = x => x + 1; // Ok Func<double,int> f3 = x => x + 1; // Error Func<int, Task<int>> f4 = async x => x + 1; // Ok
El parámetro y los tipos devueltos de cada función anónima se determinan a partir del tipo de la variable a la que se asigna la función anónima.
La primera asignación convierte correctamente la función anónima en el tipo
Func<int,int>
delegado porque, cuandox
se especifica el tipoint
,x + 1
es una expresión válida que se puede convertir implícitamente al tipoint
.Del mismo modo, la segunda asignación convierte correctamente la función anónima en el tipo delegado Func<int,double> porque el resultado de
x + 1
(de tipoint
) se convierte implícitamente en el tipodouble
.Sin embargo, la tercera asignación es un error en tiempo de compilación porque, cuando
x
se da el tipodouble
, el resultado dex + 1
(de tipodouble
) no se puede convertir implícitamente al tipoint
.La cuarta asignación convierte correctamente la función asincrónica anónima en el tipo
Func<int, Task<int>>
de delegado porque el resultado dex + 1
(de tipoint
) se puede convertir implícitamente al tipoint
de valor devuelto efectivo de la lambda asincrónica, que tiene un tipoTask<int>
de valor devuelto .ejemplo final
Una expresión F
lambda es compatible con un tipo Expression<D>
de árbol de expresión si F
es compatible con el tipo D
delegado . Esto no se aplica a métodos anónimos, solo expresiones lambda.
Las funciones anónimas pueden influir en la resolución de sobrecargas y participar en la inferencia de tipos. Consulte §12.6 para obtener más información.
10.7.2 Evaluación de conversiones de funciones anónimas a tipos delegados
La conversión de una función anónima a un tipo delegado genera una instancia de delegado que hace referencia a la función anónima y al conjunto (posiblemente vacío) de variables externas capturadas que están activas en el momento de la evaluación. Cuando se invoca el delegado, se ejecuta el cuerpo de la función anónima. El código del cuerpo se ejecuta mediante el conjunto de variables externas capturadas a las que hace referencia el delegado. Un delegate_creation_expression (§12.8.17.6) se puede usar como sintaxis alternativa para convertir un método anónimo en un tipo delegado.
La lista de invocación de un delegado generado a partir de una función anónima contiene una sola entrada. No se especifican el objeto de destino exacto y el método de destino del delegado. En concreto, no se especifica si el objeto de destino del delegado es null
, el this
valor del miembro de función envolvente o algún otro objeto.
Las conversiones de funciones anónimas semánticamente idénticas con el mismo conjunto (posiblemente vacío) de instancias de variables externas capturadas en los mismos tipos de delegado se permiten (pero no son necesarios) para devolver la misma instancia de delegado. El término semánticamente idéntico se usa aquí para significar que la ejecución de las funciones anónimas producirá, en todos los casos, los mismos efectos dados los mismos argumentos. Esta regla permite optimizar el código como el siguiente.
delegate double Function(double x);
class Test
{
static double[] Apply(double[] a, Function f)
{
double[] result = new double[a.Length];
for (int i = 0; i < a.Length; i++)
{
result[i] = f(a[i]);
}
return result;
}
static void F(double[] a, double[] b)
{
a = Apply(a, (double x) => Math.Sin(x));
b = Apply(b, (double y) => Math.Sin(y));
...
}
}
Dado que los dos delegados de función anónima tienen el mismo conjunto (vacío) de variables externas capturadas y, dado que las funciones anónimas son semánticamente idénticas, el compilador puede hacer que los delegados hagan referencia al mismo método de destino. De hecho, el compilador puede devolver la misma instancia de delegado de ambas expresiones de función anónimas.
10.7.3 Evaluación de conversiones de expresiones lambda a tipos de árbol de expresiones
La conversión de una expresión lambda a un tipo de árbol de expresión genera un árbol de expresión (§8.6). De forma más precisa, la evaluación de la conversión de expresiones lambda genera una estructura de objeto que representa la estructura de la propia expresión lambda.
No todas las expresiones lambda se pueden convertir en tipos de árbol de expresión. La conversión a un tipo de delegado compatible siempre existe, pero puede producirse un error en tiempo de compilación por motivos definidos por la implementación.
Nota: Entre las razones comunes para que una expresión lambda no se convierta en un tipo de árbol de expresión se incluyen:
- Tiene un cuerpo de bloque
- Tiene el
async
modificador- Contiene un operador de asignación
- Contiene un parámetro de salida o referencia.
- Contiene una expresión enlazada dinámicamente
nota final
10.8 Conversiones de grupo de métodos
Existe una conversión implícita de un grupo de métodos (§12.2) a un tipo de delegado compatible (§20.4). Si D
es un tipo de delegado y E
es una expresión que se clasifica como un grupo de métodos, D
entonces es compatible con E
si y solo si E
contiene al menos un método que sea aplicable en su forma normal (§12.6.4.2) a cualquier lista de argumentos (§12.6.2) que tenga tipos y modificadores que coincidan con los tipos de parámetro y modificadores de D
, como se describe en lo siguiente.
La aplicación en tiempo de compilación de la conversión de un grupo E
de métodos a un tipo D
delegado se describe en lo siguiente.
- Se selecciona un único método correspondiente a una invocación de método
M
(§12.8.10.2) del formularioE(A)
, con las siguientes modificaciones:- La lista
A
de argumentos es una lista de expresiones, cada una clasificada como una variable y con el tipo y modificador (in
,out
oref
) del parámetro correspondiente en el parameter_list deD
, excepto los parámetros de tipodynamic
, donde la expresión correspondiente tiene el tipoobject
en lugar dedynamic
. - Los métodos candidatos considerados son solo los métodos aplicables en su forma normal y no omiten ningún parámetro opcional (§12.6.4.2). Por lo tanto, los métodos candidatos se omiten si solo son aplicables en su forma expandida, o si uno o varios de sus parámetros opcionales no tienen un parámetro correspondiente en
D
.
- La lista
- Se considera que existe una conversión si el algoritmo de §12.8.10.2 genera un único método
M
que es compatible (§20.4) conD
. - Si el método seleccionado es un método
M
de instancia, la expresión de instancia asociada aE
determina el objeto de destino del delegado. - Si el método seleccionado es un método
M
de extensión que se indica mediante un acceso de miembro en una expresión de instancia, esa expresión de instancia determina el objeto de destino del delegado. - El resultado de la conversión es un valor de tipo
D
, es decir, un delegado que hace referencia al método seleccionado y al objeto de destino.
Ejemplo: a continuación se muestran las conversiones de grupo de métodos:
delegate string D1(object o); delegate object D2(string s); delegate object D3(); delegate string D4(object o, params object[] a); delegate string D5(int i); class Test { static string F(object o) {...} static void G() { D1 d1 = F; // Ok D2 d2 = F; // Ok D3 d3 = F; // Error – not applicable D4 d4 = F; // Error – not applicable in normal form D5 d5 = F; // Error – applicable but not compatible } }
La asignación para
d1
convertir implícitamente el grupoF
de métodos en un valor de tipoD1
.La asignación para
d2
mostrar cómo es posible crear un delegado en un método que tenga tipos de parámetros menos derivados (contravariantes) y un tipo de valor devuelto más derivado (covariante).La asignación para
d3
mostrar cómo no existe ninguna conversión si el método no es aplicable.La asignación para
d4
mostrar cómo debe aplicarse el método en su forma normal.La asignación para
d5
mostrar cómo se permiten los tipos de parámetro y valor devuelto del delegado y el método solo para los tipos de referencia.ejemplo final
Al igual que con las demás conversiones implícitas y explícitas, el operador de conversión se puede usar para realizar explícitamente una conversión determinada.
Ejemplo: Por lo tanto, el ejemplo
object obj = new EventHandler(myDialog.OkClick);
podría escribirse en su lugar
object obj = (EventHandler)myDialog.OkClick;
ejemplo final
Una conversión de grupo de métodos puede hacer referencia a un método genérico, ya sea especificando explícitamente argumentos de tipo dentro E
de o a través de la inferencia de tipos (§12.6.3). Si se usa la inferencia de tipos, los tipos de parámetro del delegado se usan como tipos de argumento en el proceso de inferencia. El tipo de valor devuelto del delegado no se usa para la inferencia. Si se especifican o deducen los argumentos de tipo, forman parte del proceso de conversión del grupo de métodos; son los argumentos de tipo que se usan para invocar el método de destino cuando se invoca el delegado resultante.
Ejemplo:
delegate int D(string s, int i); delegate int E(); class X { public static T F<T>(string s, T t) {...} public static T G<T>() {...} static void Main() { D d1 = F<int>; // Ok, type argument given explicitly D d2 = F; // Ok, int inferred as type argument E e1 = G<int>; // Ok, type argument given explicitly E e2 = G; // Error, cannot infer from return type } }
ejemplo final
Los grupos de métodos pueden influir en la resolución de sobrecargas y participar en la inferencia de tipos. Consulte §12.6 para obtener más información.
La evaluación en tiempo de ejecución de una conversión de grupo de métodos continúa de la siguiente manera:
- Si el método seleccionado en tiempo de compilación es un método de instancia, o es un método de extensión al que se tiene acceso como método de instancia, el objeto de destino del delegado se determina a partir de la expresión de instancia asociada a
E
:- Se evalúa la expresión de instancia. Si esta evaluación provoca una excepción, no se ejecutan pasos adicionales.
- Si la expresión de instancia es de un reference_type, el valor calculado por la expresión de instancia se convierte en el objeto de destino. Si el método seleccionado es un método de instancia y el objeto de destino es
null
, se inicia ySystem.NullReferenceException
no se ejecutan más pasos. - Si la expresión de instancia es de un value_type, se realiza una operación de conversión boxing (§10.2.9) para convertir el valor en un objeto y este objeto se convierte en el objeto de destino.
- De lo contrario, el método seleccionado forma parte de una llamada al método estático y el objeto de destino del delegado es
null
. - Se obtiene una instancia de delegado de tipo
D
delegado con una referencia al método que se determinó en tiempo de compilación y una referencia al objeto de destino calculado anteriormente, como se indica a continuación:- La conversión se permite (pero no necesaria) para usar una instancia de delegado existente que ya contiene estas referencias.
- Si no se ha reutilizado una instancia existente, se crea una nueva (§20.5). Si no hay suficiente memoria disponible para asignar la nueva instancia, se produce una
System.OutOfMemoryException
excepción . De lo contrario, la instancia se inicializa con las referencias especificadas.
ECMA C# draft specification