Nota
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
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
inta tipolonges implícita, por lo que las expresiones de tipointse pueden tratar implícitamente como tipolong. La conversión opuesta, de tipolonga 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 intejemplo 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 miembros de función (§12.6.6), expresiones de conversión (§12.9.8) y asignaciones (§12.23).
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
TyT, para cualquier tipoT. - Entre
TyT?para cualquier tipoTde referencia . - Entre
objectydynamic. - 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
t1t2yt3todos tienen dos elementos: unintseguido de .stringLos tipos de elementos de tupla pueden usarse por tuplas, como ent4,t5yt6. 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,t5yt6.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 indica el tipo (§12.9.8).
10.2.3 Conversiones numéricas implícitas
Las conversiones numéricas implícitas son:
- De
sbyteashort,int,long,float,doubleodecimal. - De
byteashort,ushort, ,intuint,long,ulong,floatdoubleodecimal. - De
shortaint,long,float,doubleodecimal. - De
ushortaint, ,uintlong,ulong,float,doubleodecimal. - De
intalong,float,doubleodecimal. - De
uintalong,ulong,float,doubleodecimal. - De
longafloat,doubleodecimal. - De
ulongafloat,doubleodecimal. - De
charaushort,int, ,uintlong,ulong,float, ,doubleodecimal. - De
floatadouble.
Las conversiones de , intuinto long a ulong y desde floatlongo 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.25) 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
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
objectydynamic. - De cualquier class_type
Sa cualquier class_typeT, se derivaSdeT. - Desde cualquier , proporcionado
Simplementa . - De cualquier interface_type
Sa cualquier interface_typeT, se derivaSdeT. - De un con un tipo
Sde elemento a unSᵢcon un tipoTde elemento , siempre que se cumplan todas las siguientes condiciones:-
SyTsolo difieren en el tipo de elemento. En otras palabras,SyTtienen 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 deSaT. - Desde cualquier array_type hacia
System.Arrayy las interfaces que implementa. - Desde cualquier delegate_type hacia
System.Delegatey las interfaces que implementa. - Desde el literal null (§6.4.5.7) a cualquier tipo de referencia.
- De cualquier
- De cualquier reference_type a un tipo
Tde 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 (§19.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
- De cualquier non_nullable_value_type a cualquier interface_type
Ide modo que haya una conversión boxing de la non_nullable_value_type a otra interface_typeI₀, yI₀es convertible de varianza (§19.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 SinterfazI, 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
vde tipoSconsiste 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
Stiene el tipoSen tiempo de ejecución y una comprobación de tipo en tiempo de ejecución mediante elisoperador 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 intUna 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 tipoobjectmenos 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
pparaboxhacer que se copie el valor dep. Se habíaPointdeclarado enclasssu lugar, el valor 20 sería de salida porquepyboxharí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 objecty, 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 existsLas asignaciones a
s2yiambas 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 (d) al tipo destringdestino. 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.25) de tipo
intse puede convertir en el tiposbyte, ,byteshort,ushort,uintoulong, siempre que el valor del constant_expression esté dentro del intervalo del tipo de destino. - Un constant_expression de tipo se puede convertir en el tipo
long, siempre que el valor delulongno sea negativo.
10.2.12 Conversiones implícitas que implican parámetros de tipo
Para un type_parameterT que se sabe que es un tipo de referencia (§15.2.5), existen las siguientes conversiones de referencia implícitas (§10.2.8):
- De
Ta su claseCbase efectiva , deTa cualquier clase base deCy deTa cualquier interfaz implementada porC. - De
Ta un interface_typeIde laTinterfaz efectiva establecida y deTa cualquier interfaz base deI. - De
Ta un parámetroUde tipo proporcionado queTdepende deU(§15.2.5).Nota: Dado
Tque se sabe que es un tipo de referencia, dentro del ámbito deT, el tipo en tiempo de ejecución deUsiempre será un tipo de referencia, aunqueUno 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_parameterT 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
Ta su claseCbase efectiva , deTa cualquier clase base deCy deTa cualquier interfaz implementada porC.Nota:
Cserá uno de los tiposSystem.Object,System.ValueTypeoSystem.Enum(de lo contrarioT, se sabe que es un tipo de referencia). nota final - De
Ta un interface_typeIde laTinterfaz efectiva establecida y deTa cualquier interfaz base deI.
Para un type_parameterT 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 Tde tipo determinado:
- De a un tipo de
Treferencia si tiene una conversión implícita a un tipoSde referencia yS₀tiene una conversión de identidad aS₀.SEn tiempo de ejecución, la conversión se ejecuta de la misma manera que la conversión aS₀. - De a un tipo de interfaz si tiene una conversión implícita a un tipo
Tde interfaz yIes variable de varianza aI₀(I₀).IEn tiempo de ejecución, siTes 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 Ttipo 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 ignoredLas declaraciones de
t1,t2t4yt5son válidas, ya que existen conversiones implícitas de las expresiones de elemento a los tipos de elemento correspondientes. La declaración det3no es válida, porque no hay ninguna conversión denullaint. La declaración det5provoca 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.2.18 Conversión de expresiones switch
Hay una conversión implícita de un switch_expression (§12.11) a cada tipo T para el que existe una conversión implícita de cada switch_expression_arm_expression de switch_expression_arm a .T
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.8).
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
sbyteabyte,ushort,uint,ulongochar. - De
byteasbyteochar. - De
shortasbyte,byte,ushort,uint,ulongochar. - De
ushortasbyte,byte,shortochar. - De
intasbyte, ,byteshort,ushort,uint,ulongochar. - De
uintasbyte,byte,short,ushort,intochar. - De
longasbyte,byte, ,shortushort,int,uint, ,ulongochar. - De
ulongasbyte,byte, ,shortushort,int,uint, ,longochar. - De
charasbyte,byteoshort. - De
floatasbyte, ,byte,shortushort,int,uintlongulongcharo .decimal - De
doubleasbyte, ,byte,shortushort,int,uint,long,ulong,char,floatodecimal. - De
decimalasbyte, ,byte,shortushort,int,uint,long,ulong,char,floatodouble.
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.8).
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
checkedcontexto, la conversión se realiza correctamente si el valor del operando de origen está dentro del intervalo del tipo de destino, pero produce unSystem.OverflowExceptionsi el valor del operando de origen está fuera del intervalo del tipo de destino. - En un
uncheckedcontexto, 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
decimala 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.OverflowExceptionexcepción . - Para una conversión de
floatodoublea 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.OverflowExceptionexcepció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
doubleafloat, eldoublevalor se redondea al valor más cercanofloat. Si eldoublevalor es demasiado pequeño para representar comofloat, el resultado se convierte en cero con el mismo signo que el valor. Si la magnitud deldoublevalor es demasiado grande para representar comofloat, el resultado se convierte en infinito con el mismo signo que el valor. Si eldoublevalor es NaN, el resultado también es NaN. - Para una conversión de
floatodoubleadecimal, el valor de origen se convierte endecimalrepresentació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 sidecimaladmite valores cero firmados. - Si la magnitud del valor de origen es demasiado grande para representar como ,
decimalo 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
decimalfloatodouble, eldecimalvalor se redondea al valor más cercanodoubleofloat. 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
decimaltipo no es necesario para admitir las infinities o los valores NaN, pero puede hacerlo; su intervalo puede ser menor que el intervalo defloatydouble, pero no se garantiza que sea. Paradecimalrepresentaciones sin infinities o valores NaN, y con un intervalo menor quefloat, el resultado de una conversión dedecimala ofloatdoublenunca 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, ,ushortint,uint,longulongcharfloat, odoubledecimala cualquier enum_type. - De cualquier enum_type a
sbyte,byte,shortushortintuintlongulongchar,float, ,doubleo .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
Econ un tipo subyacente deint, se procesa una conversión deEabytecomo conversión numérica explícita (§10.3.2) deintabyte, y una conversión debyteaEse procesa como una conversión numérica implícita (§10.2.3) debyteaint. 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
Sa cualquier class_typeT, proporcionadoSes una clase base deT. - Desde cualquier , siempre
Sque no esté sellado y proporcionado no implemente .T - Desde cualquier a cualquier
S, siempreTque no esté sellado o proporcionadoTimplementeT. - De cualquier interface_type
Sa cualquier interface_typeT, proporcionadoSno se deriva deT. - De un con un tipo
Sde elemento a unSᵢcon un tipoTde elemento , siempre que se cumplan todas las siguientes condiciones:-
SyTsolo difieren en el tipo de elemento. En otras palabras,SyTtienen el mismo número de dimensiones. - Existe una conversión de referencia explícita de
SᵢaTᵢ.
-
- Desde
System.Arrayy las interfaces que implementa, en cualquier array_type. - Desde un array_typea
S[],System.Collections.Generic.IList<T>y sus interfaces base, siempre que haya una conversión de identidad o una conversión de referencia explícita deSystem.Collections.Generic.IReadOnlyList<T>aS. - 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 deSa T. - Desde
System.Delegatey las interfaces que implementa en cualquier delegate_type. - Desde un tipo
Sde referencia a un tipoTde referencia si tiene una conversión de referencia explícita deSa un tipoT₀de referencia yT₀hay una conversión de identidad deT₀aT. - Desde un tipo
Sde referencia a una interfaz o un tipoTdelegado si hay una conversión de referencia explícita deSa una interfaz o un tipoT₀delegado y esT₀variable de varianza aT§TT₀19.2.3.3. - De
D<S₁...Sᵥ>a dondeD<T₁...Tᵥ>D<X₁...Xᵥ>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 deDlas 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á nullo 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 Ttipo 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
objecthasta cualquier value_type. - Desde el tipo
System.ValueTypehasta cualquier value_type. - Desde el tipo
System.Enumhasta cualquier enum_type. - Desde cualquier interface_type a cualquier non_nullable_value_type que implemente el interface_type.
- Desde cualquier
Idonde haya una conversión unboxing de un y una conversión de identidad de a .I₀ - Desde cualquier interface_type
Ia 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 aIoIse convierta en varianza aI₀(§19.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 nullo 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.9ejecutar la expresión . Por lo tanto, las instrucciones
object 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_parameterT 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
Cbase efectiva deTaTy de cualquier clase base deCaT. - De cualquier interface_type a
T. - De
Ta cualquier interface_typeIproporcionado ya no hay una conversión de referencia implícita deTaI. - De un a
Usiempre queTdependaTde (U).Nota: Dado
Tque 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, aunqueUno se sepa que es un tipo de referencia en tiempo de compilación. nota final
Para un type_parameterT 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
Cbase efectiva deTaTy de cualquier clase base deCaT.Nota: C será uno de los tipos
System.Object,System.ValueTypeoSystem.Enum(de lo contrarioT, se sabe que es un tipo de referencia). nota final - De cualquier interface_type a
T.
Para un type_parameterT que no se sabe que es un tipo de referencia (§15.2.5), existen las siguientes conversiones explícitas:
- De
Ta cualquier interface_typeIsiempre que no haya una conversión implícita deTaI. Esta conversión consta de una conversión boxing implícita (§10.2.9) de aTseguida deobjectuna conversión de referencia explícita deobjectaI. En tiempo de ejecución, siTes 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, siTes un tipo de referencia, la conversión se ejecuta como una conversión de referencia explícita. - De un parámetro
Ude tipo aTproporcionado queTdepende deU(§15.2.5). En tiempo de ejecución, siTes un tipo de valor yUes un tipo de referencia, la conversión se ejecuta como una conversión de unboxing. En tiempo de ejecución, si ambosTyUson tipos de valor,TyUson necesariamente el mismo tipo y no se realiza ninguna conversión. En tiempo de ejecución, siTes un tipo de referencia, entoncesUtambié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
talong, 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 cuadrointno se puede convertir directamente en .longejemplo 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
Aa un tipoB, existe una conversión explícita estándar de tipoAa tipoBy de tipoBa 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 ST 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 ST de destino solo si se cumplen todas las siguientes condiciones:
-
S₀yT₀son tipos diferentes. -
S₀OT₀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
SaTo deTaS.
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
Aa un tipoBy, si niAniBson interface_types, se dice queAabarcaBy se dice queBabarcaA. - Si existe una conversión implícita estándar (§10.4.2) desde una expresión
Ea un tipoB, y si niBni el tipo deE(si lo tiene) son de tipo de interfaz , entonces se dice queEestá abarcado porB, y se dice queBabarcaE. - 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
SyS₀T₀.- Si
Etiene un tipo, vamosSa ser ese tipo. - Si
SoTson tipos de valor que aceptan valores NULL, letSᵢyTᵢsean sus tipos subyacentes, de lo contrario, letSᵢy beTᵢyST, respectivamente. - Si
SᵢoTᵢson parámetros de tipo, letS₀yT₀sean sus clases base efectivas, de lo contrario, let yS₀beT₀ySᵢTᵢ, respectivamente.
- Si
Busque el conjunto de tipos, ,
Ddesde 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 conjuntoDsolo 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 deDque convierten de un tipo que abarcaEa un tipo incluido enT. SiUestá 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
Sexiste y cualquiera de los operadores deUconversió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
UT,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
Ucontiene 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
Ucontiene 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 deEaSₓ. - El operador de conversión más específico se invoca para convertir de
SₓaTₓ. - Si
TₓnoTes , 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
SyS₀T₀.- Si
Etiene un tipo, vamosSa ser ese tipo. - Si
SoTson tipos de valor que aceptan valores NULL, letSᵢyTᵢsean sus tipos subyacentes, de lo contrario, letSᵢy beTᵢyST, respectivamente. - Si
SᵢoTᵢson parámetros de tipo, letS₀yT₀sean sus clases base efectivas, de lo contrario, let yS₀beT₀ySᵢTᵢ, respectivamente.
- Si
- Busque el conjunto de tipos, ,
Ddesde 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). Un tipo se agrega al conjuntoDsolo 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 o explícitos definidos por el usuario declarados por las clases o estructuras enDque se convierten de un tipo que abarcaEo abarca (Ssi existe) a un tipo que abarca o abarca .TSiUestá 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
Uconversión deS, esSₓS. - De lo contrario, si alguno de los operadores de
Uconversión de tipos que abarcan , esEel tipo más abarcadoSₓ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
UT,TₓesT. - De lo contrario, si alguno de los operadores de
Uconversió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
Ucontiene 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
Eaú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ₓnoTes , 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
Una conversión que acepta valores NULL permite usar una conversión predefinida que opera en un tipo de valor que no acepta valores NULL para usarse también con la forma que acepta valores NULL de ese tipo. 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 ST 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
SaT? - 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 (
HasValuela 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 deSaT, seguida de un ajuste deTaT?.
- Si el valor de origen es null (
- Si la conversión que acepta valores NULL es de
SaT?, la conversión se evalúa como la conversión subyacente deSaTseguida de un ajuste deTaT?. - Si la conversión que acepta valores NULL es de
S?aT, la conversión se evalúa como una desencapsulación deS?aSseguida de la conversión subyacente deSaT.
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 Tde 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 S seguida de T 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.21). 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
Fcontiene un anonymous_function_signature,DyFtienen el mismo número de parámetros. - Si
Fno contiene un anonymous_function_signature,Dpuede tener cero o más parámetros de cualquier tipo, siempre y cuando ningún parámetro de sea un parámetro deDsalida. - Si
Ftiene una lista de parámetros con tipo explícito, cada parámetro deDtiene los mismos modificadores que el parámetro correspondiente enFy existe una conversión de identidad entre el parámetro correspondiente enF. - Si
Ftiene una lista de parámetros con tipo implícito,Dno tiene parámetros de referencia ni de salida. - Si el cuerpo de
Fes una expresión y o bienDtiene un tipo de valor devuelto nulo oFes asincrónico yDtiene un tipo de valor devuelto«TaskType»(§15.14.1), cuando cada parámetro deFtiene el tipo del parámetro correspondiente enD, el cuerpo deFes una expresión válida (w.r.t §12) que se permitiría como statement_expression (§13.7). - Si el cuerpo de
Fes un bloque y tiene un tipoDes asincrónico yFtiene unDtipo de valor devuelto , cuando cada parámetro de«TaskType»recibe el tipo del parámetro correspondiente enF, el cuerpo deDes un bloque válido (w.r.tF) en el que ninguna instrucción especifica una expresión. - Si el cuerpo de
Fes una expresión y o bienFno es asincrónico yDtiene un tipo de valor devueltovoidT, oFes asincrónico yDtiene un tipo de valor devuelto«TaskType»<T>(§15.14.1), cuando cada parámetro deFtiene el tipo del parámetro correspondiente enD, el cuerpo deFes una expresión válida (w.r.t §12) que se puede convertir implícitamente enT. - Si el cuerpo de
Fes un bloque y noFes asincrónico yDtiene un tipoTde valor devuelto no nulo , oFes asincrónico yDtiene un«TaskType»<T>tipo de valor devuelto, cuando cada parámetro deFse asigna el tipo del parámetro correspondiente enD, el cuerpo de es un bloque deFinstrucciones 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 convertirTimplí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 tipoAy 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; // OkEl 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, cuandoxse especifica el tipoint,x + 1es 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
xse 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 tipointde 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 Ddelegado . 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.5) 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, se permite que un compilador haga referencia a los delegados al mismo método de destino. De hecho, se permite que un compilador devuelva 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
asyncmodificador- 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 desde un grupo de métodos (§12.2) a un tipo de delegado compatible (§21.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
Ade argumentos es una lista de expresiones, cada una clasificada como una variable y con el tipo y modificador (in,outoref) del parámetro correspondiente en el parameter_list deD, excepto los parámetros de tipodynamic, donde la expresión correspondiente tiene el tipoobjecten 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
Mmejor que es compatible (§21.4) conD. - Si el método seleccionado es un método
Mde instancia, la expresión de instancia asociada aEdetermina el objeto de destino del delegado. - Si el método seleccionado es un método
Mde 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
d1convertir implícitamente el grupoFde métodos en un valor de tipoD1.La asignación para
d2mostrar 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
d3mostrar cómo no existe ninguna conversión si el método no es aplicable.La asignación para
d4mostrar cómo debe aplicarse el método en su forma normal.La asignación para
d5mostrar 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 Ede 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.NullReferenceExceptionno 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
Ddelegado 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 (§21.5). Si no hay suficiente memoria disponible para asignar la nueva instancia, se produce una
System.OutOfMemoryExceptionexcepción . De lo contrario, la instancia se inicializa con las referencias especificadas.
ECMA C# draft specification