Что-то случилось со мной сегодня утром, что заставило меня почесать затылок.
Любая переменная типа 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?)
(вместо того, чтобы выдавать NullReferenceException
).
Итак, я немного покопался и, конечно же, оказалось, что это поведение специфично для типа Nullable<T>
, специально определенного в спецификациях C # и VB.NET, и не воспроизводится ни в каких пользовательских struct
(C #) или Structure
( VB.NET).
Вот почему я до сих пор в замешательстве.
Это конкретное поведение упаковки и распаковки невозможно реализовать вручную. Это работает только потому, что и C #, и VB.NET уделяют особое внимание типу Nullable<T>
.
Разве теоретически не возможно, чтобы существовал другой язык на основе интерфейса командной строки, где
Nullable<T>
не пользовались этой специальной обработкой? И не будет ли поэтому типNullable<T>
проявлять разное поведение на разных языках?Как C # и VB.NET достигают такого поведения? Поддерживается ли это CLR? (То есть позволяет ли CLR типу каким-либо образом «переопределять» способ упаковки, даже если сами C # и VB.NET это запрещают?)
Возможно ли вообще (в C # или VB.NET) упаковать
Nullable<T>
какobject
?