Что-то мне пришло в голову раньше сегодня, и я почесал голову.
Любую переменную типа Nullable<T>
можно присвоить null
. Например:
int? i = null;
Сначала я не мог понять, как это возможно, без какого-либо определения неявного преобразования от object
до Nullable<T>
:
public static implicit operator Nullable<T>(object box);
Но вышеприведенный оператор явно не существует, как если бы он это сделал, тогда также должно было бы быть законным, по крайней мере, во время компиляции (чего это не так):
int? i = new object();
Тогда я понял, что, возможно, тип Nullable<T>
может определить неявное преобразование в какой-либо произвольный ссылочный тип, который никогда не может быть создан, например:
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();
}
}
Однако это не объясняет, что произошло со мной следующим: если свойство HasValue
false
для любого значения Nullable<T>
, тогда это значение будет помечено как null
:
int? i = new int?();
object x = i; // Now x is null.
Кроме того, если HasValue
- true
, тогда значение будет помещаться как T
, а не T?
:
int? i = 5;
object x = i; // Now x is a boxed int, NOT a boxed Nullable<int>.
Но это, по-видимому, подразумевает, что существует неявное преобразование с Nullable<T>
в object
:
public static implicit operator object(Nullable<T> value);
Это явно не так, поскольку object
является базовым классом для всех типов, а пользовательские неявные преобразования в/из базовых типов являются незаконными (также они должны быть).
Кажется, что object x = i;
должен помещать i
как любой другой тип значения, так что x.GetType()
даст тот же результат, что и typeof(int?)
(а не бросает a NullReferenceException
).
Итак, я вырыл бит и, конечно же, выяснилось, что это поведение специфично для типа Nullable<T>
, специально определенного как в спецификациях С#, так и в VB.NET, и не воспроизводимо в любом пользовательском struct
(С#) или Structure
(VB.NET).
Вот почему я все еще смущен.
Это конкретное поведение в боксе и распаковке представляется невозможным для реализации вручную. Он работает только потому, что и С#, и VB.NET обеспечивают специальное обращение к типу Nullable<T>
.
-
Не теоретически возможно, что может существовать другой язык на основе CLI, где
Nullable<T>
не было предоставлено это специальное лечение? И не будет ли типNullable<T>
проявлять различное поведение на разных языках? -
Как С# и VB.NET достигают такого поведения? Поддерживается ли это CLR? (То есть, CLR позволяет типу каким-то образом "переопределять" способ, которым он помещен в коробку, даже если сами С# и VB.NET запрещают его?)
-
Возможно ли (в С# или VB.NET) вставить a
Nullable<T>
в качествеobject
?