¿Por qué Nullable es nullable? ¿Por qué no se puede reproducir?

Cuando yo escribo

Nullable<Nullable> test = null;

Me sale un error de comstackción:

The type 'System.Datetime?' must be a non-nullable value type in order to use it as a paramreter 'T' in the generic type or method 'System.Nullable'

Pero Nullable es una struct por lo que se supone que no puede ser nullable.

Así que traté de crear esta struct :

 public struct Foo where T : struct { private T value; public Foo(T value) { this.value = value; } public static explicit operator Foo(T? value) { return new Foo(value.Value); } public static implicit operator T?(Foo value) { return new Nullable(value.value); } } 

Ahora cuando escribo

  Nullable<Foo> test1 = null; Foo<Nullable> test2 = null; Foo test3 = null; 

La primera línea está bien, pero para la segunda y tercera línea obtengo los dos errores de comstackción siguientes:

The type 'System.DateTime?' must be a non-nullable value type in order to use it as a parameter 'T' in the generic type or method 'MyProject.Foo' The type 'System.DateTime?' must be a non-nullable value type in order to use it as a parameter 'T' in the generic type or method 'MyProject.Foo' (solo segunda línea)

y

Cannot convert null to 'MyProject.Foo because it is a non-nullable value type'

  Foo<Nullable> test = new Foo(); 

no funciona ningún evento si Nullable es una struct .

Conceptualmente, puedo entender por qué Nullable es anulable, evita tener cosas como DateTime?????????? sin embargo, todavía puedo tener List<List<List<List<List>>>>

Entonces, ¿por qué esta limitación y por qué no puedo reproducir este comportamiento en Foo ? ¿Es esta limitación impuesta por el comstackdor o es intrínseca en el Nullable de Nullable ?

Leí esta pregunta, pero solo dice que no es posible, ninguna de las respuestas dice fundamentalmente por qué no es posible.

Pero Nullable es una estructura por lo que se supone que no puede ser nullable.

Nullable es de hecho una estructura, pero el significado preciso de la restricción de struct genérica como se indica en los documentos es:

El argumento de tipo debe ser un tipo de valor. Se puede especificar cualquier tipo de valor, excepto Nullable . Consulte Uso de tipos anulables (Guía de progtwigción de C #) para obtener más información.

Por el mismo motivo, su línea.

 Foo> test2 = null; 

da como resultado el error de comstackción que está viendo, porque su restricción de struct genérica restringe su argumento T genérico de una manera que no puede especificarse como Nullable como un argumento real.

Una razón para esto puede haber sido hacer llamadas como

 Nullable> test = null; 

menos ambiguo: ¿ test.HasValue quiere decir que desea establecer test.HasValue en false , o realmente desea configurar test.HasValue en true y test.Value.HasValue en false ? Con la restricción dada a los argumentos de tipo no anulable, esta confusión no se produce.

Finalmente, la asignación null funciona con Nullable porque, como lo Nullable las respuestas seleccionadas y sus comentarios a esta pregunta SO y esta pregunta SO , el tipo Nullable es compatible con la magia de un comstackdor.

El error está diciendo que el parámetro de tipo de Nullable no debe ser nullable.

Lo que estás haciendo es crear un tipo de Nullable que tiene un parámetro de tipo que puede ser anulado, que no está permitido:

 Nullable> 

es lo mismo que

 Nullable 

Lo cual es bastante inútil. ¿Por qué desea tener un tipo anulable para un tipo que ya es anulable?

Nullable es solo un tipo que se ha introducido en .NET 2.0 para que pueda utilizar los ‘tipos de valores anulables’. Por ejemplo, si tiene un método que tiene un parámetro datetime que es opcional; En lugar de pasar un ‘valor mágico’ como DateTime.MinValue, ahora puede pasarle un valor nulo a ese método si no desea usar ese parámetro.

En las clases genéricas where T: struct significa que el tipo T no puede ser nulo. Sin embargo, los tipos anulables están diseñados para agregar nulabilidad a las estructuras. Técnicamente son estructuras, pero se comportan como si pudieran contener un valor nulo. Debido a esta ambigüedad, el uso de nullables no está permitido con la restricción where T: struct – vea Restricciones en los parámetros de tipo

Los tipos anulables no son solo estructuras genéricas con soporte especial de comstackdor de C #. Los tipos anulables son compatibles con CLR (ver CLR a través de C # por Jeffrey Richter), y parece que este soporte especial de CLR los hace no recursivos.

  • ¿CLR es compatible con las reglas especiales de box / unboxing int? i = 1; object o = i int? i = 1; object o = i int? i = 1; object o = i pondré el valor int en la variable o y no el Nullable . En caso de nullables múltiples, debe o = (int??)1; contiene int o int? ¿valor?
  • CLR tiene soporte especial para llamar a miembros de GetType y de interfaz, llama a métodos de tipo subyacente. Esto realmente lleva a una situación cuando Nullable.GetType () lanza una excepción NullObjectReference, cuando tiene NullValueFlag.

En cuanto a C #, hay muchas características en C # que están codificadas para los tipos anulables. Basado en este artículo Tipos anulables (Guía de progtwigción de C #), el objective principal de introducir tipos anulables es agregar compatibilidad nula para los tipos que no admiten nulos. Lógicamente, desde DateTime? ya es compatible con valores nulos, no se debe permitir que sea “más” que pueda ser anulable.

Este documento también establece claramente que

No se permiten tipos anulables nesteds. La siguiente línea no se comstackrá: Nullable> n;

Características especiales de C # de tipos anulables:

  • C # tiene especial ?? operador. ¿Debería (int???)null ?? (int)1 (int???)null ?? (int)1 resuelve a (int??)1 o a (int)1 valor?
  • Nullables tiene una propiedad especial System.Nullable.GetValueOrDefault . ¿Qué debería devolver para las nullables anidadas?
  • Procesamiento especial para ? == null ? == null y ? != null ? != null operadores ? != null . Si el valor de Nullable> contiene el valor de Nullable , pero este valor es nulo, ¿qué debe devolver la propiedad HasValue? ¿Cuál debería ser el resultado de la comparación con null?
  • Conversiones implícitas especiales. ¿Debería int?? i = 10 int?? i = 10 ser convertible implícitamente?
  • Conversiones explícitas. Debería ser int i = (int??)10; ¿soportado?
  • Apoyo especial para bool? type Utilizando tipos anulables . Por ejemplo, (bool?)null | (bool?)true == true (bool?)null | (bool?)true == true .

Entonces, ¿debería CLR soportar una llamada GetType () recursiva? ¿Debería eliminar las envolturas de Nullable cuando el valor del boxeo? Si debería funcionar con valores de un nivel, ¿por qué no hacerlo también para todos los demás niveles? Demasiadas opciones a considerar, demasiados procesamientos recursivos.

La solución más fácil es hacer que Nullable> no sea comstackble.