convert - type number c#
¿Cómo es posible el comportamiento de boxing/unboxing de Nullable<T>? (3)
Has acertado: Nullable<T>
recibe un tratamiento especial del compilador, tanto en VB como en C #. Por lo tanto:
- Sí. El compilador de lenguaje necesita un caso especial
Nullable<T>
. - El compilador refactoriza el uso de
Nullable<T>
. Los operadores son solo azúcar sintáctico. - No que yo sepa.
Algo se me acaba de ocurrir el día de hoy que me hizo rascar la cabeza.
Cualquier variable de tipo Nullable<T>
se puede asignar a null
. Por ejemplo:
int? i = null;
Al principio no pude ver cómo sería posible sin definir de alguna manera una conversión implícita de object
a Nullable<T>
:
public static implicit operator Nullable<T>(object box);
Pero el operador anterior claramente no existe, como si lo hiciera, lo siguiente también debería ser legal, al menos en tiempo de compilación (que no es):
int? i = new object();
Entonces me di cuenta de que tal vez el tipo Nullable<T>
podría definir una conversión implícita a algún tipo de referencia arbitraria que nunca se pueda crear una instancia, como esta:
public abstract class DummyBox
{
private DummyBox()
{ }
}
public struct Nullable<T> where T : struct
{
public static implicit operator Nullable<T>(DummyBox box)
{
if (box == null)
{
return new Nullable<T>();
}
// This should never be possible, as a DummyBox cannot be instantiated.
throw new InvalidCastException();
}
}
Sin embargo, esto no explica lo que se me ocurrió a continuación: si la propiedad HasValue
es false
para cualquier valor Nullable<T>
, entonces ese valor se incluirá en cuadro como null
:
int? i = new int?();
object x = i; // Now x is null.
Además, si HasValue
es true
, entonces el valor será enmarcado como T
lugar de T?
:
int? i = 5;
object x = i; // Now x is a boxed int, NOT a boxed Nullable<int>.
Pero esto parece implicar que hay una conversión implícita personalizada de Nullable<T>
a object
:
public static implicit operator object(Nullable<T> value);
Claramente, este no es el caso, ya que el object
es una clase base para todos los tipos, y las conversiones implícitas definidas por el usuario hacia / desde los tipos básicos son ilegales (como deberían ser).
Parece que el object x = i;
debería cuadro i
gusta cualquier otro tipo de valor, por lo que x.GetType()
arrojaría el mismo resultado que typeof(int?)
(en lugar de arrojar una NullReferenceException
).
Así que profundicé un poco y, Nullable<T>
, resulta que este comportamiento es específico del tipo Nullable<T>
, especialmente definido en las especificaciones C # y VB.NET, y no reproducible en ninguna struct
definida por el usuario (C #) o Structure
(VB.NET).
He aquí por qué todavía estoy confundido.
Este comportamiento particular de boxeo y desembalaje parece ser imposible de implementar a mano. Solo funciona porque tanto C # como VB.NET otorgan un tratamiento especial al tipo Nullable<T>
.
¿No es teóricamente posible que exista un lenguaje diferente basado en CLI donde
Nullable<T>
no recibió este tratamiento especial? ¿Y el tipoNullable<T>
noNullable<T>
un comportamiento diferente en diferentes idiomas?¿Cómo C # y VB.NET logran este comportamiento? ¿Es compatible con CLR? (Es decir, ¿el CLR permite que un tipo "anule" de algún modo la forma en que está encuadrado, aunque C # y VB.NET por sí mismos lo prohíben?)
¿Es posible (en C # o VB.NET) encasillar un
Nullable<T>
comoobject
?
Hay dos cosas sucediendo:
1) El compilador trata "nulo" no como una referencia nula sino como un valor nulo ... el valor nulo para cualquier tipo al que necesite convertir. En el caso de un Nullable<T>
es solo el valor que tiene False para el campo / propiedad HasValue
. Entonces, si tienes una variable de tipo int?
, es muy posible que el valor de esa variable sea null
; solo necesitas cambiar tu comprensión de lo que significa null
un poquito.
2) Los tipos anulables de boxeo reciben un trato especial por parte del CLR. Esto es relevante en su segundo ejemplo:
int? i = new int?();
object x = i;
el compilador encapsulará cualquier valor de tipo anulable de forma diferente a los valores de tipo no anulable. Si el valor no es nulo, el resultado será el mismo que el de boxeo con el mismo valor que un valor de tipo no anulable, por lo que un int?
con el valor 5 se coloca el recuadro del mismo modo que con un valor int
5: se pierde la "capacidad de nulos". Sin embargo, el valor nulo de un tipo que admite nulos se encajona solo en la referencia nula, en lugar de crear un objeto en absoluto.
Esto se introdujo tarde en el ciclo de CLR v2, a petición de la comunidad.
Significa que no hay tal cosa como un "valor de tipo de valor anulable en caja".
Me estaba haciendo la misma pregunta y también esperaba tener un operador implícito para Nullable<T>
en el código fuente Null de .net, así que miré cuál es el código IL correspondiente a int? a = null;
int? a = null;
para entender lo que está sucediendo detrás de la escena:
código c #:
int? a = null;
int? a2 = new int?();
object a3 = null;
int? b = 5;
int? b2 = new int?(5);
Código IL (generado con LINQPad 5):
IL_0000: nop
IL_0001: ldloca.s 00 // a
IL_0003: initobj System.Nullable<System.Int32>
IL_0009: ldloca.s 01 // a2
IL_000B: initobj System.Nullable<System.Int32>
IL_0011: ldnull
IL_0012: stloc.2 // a3
IL_0013: ldloca.s 03 // b
IL_0015: ldc.i4.5
IL_0016: call System.Nullable<System.Int32>..ctor
IL_001B: ldloca.s 04 // b2
IL_001D: ldc.i4.5
IL_001E: call System.Nullable<System.Int32>..ctor
IL_0023: ret
Vemos que el compilador cambia int? a = null
int? a = null
a algo como int? a = new int?()
int? a = new int?()
que es bastante diferente al object a3 = null
. Entonces claramente Nullables tiene un tratamiento especial de compilación.