Обнуляемый тип не обнуляемый тип? - PullRequest
44 голосов
/ 24 апреля 2009

Я проводил некоторые тесты с обнуляемыми типами, и это не сработало так, как я ожидал:

int? testInt = 0;
Type nullableType = typeof(int?);
Assert.AreEqual(nullableType, testInt.GetType()); // not the same type

Это тоже не работает:

DateTime? test = new DateTime(434523452345);
Assert.IsTrue(test.GetType() == typeof(Nullable)); //FAIL 

DateTime? test = new DateTime(434523452345);
Assert.IsTrue(test.GetType() == typeof(Nullable<>)); //STILL FAIL

У меня вопрос, почему testInt.GetType () возвращает int, а typeof (int?) Возвращает истинный обнуляемый тип?

Ответы [ 4 ]

56 голосов
/ 24 апреля 2009

Согласно MSDN :

Вызов GetType для типа Nullable вызывает операцию бокса выполняется, когда тип неявно преобразован в объект. Поэтому GetType всегда возвращает объект Type, который представляет базовый тип, а не Обнуляемый тип.

Когда вы упаковываете обнуляемый объект, упаковывается только основной тип.

Опять от MSDN :

Бокс ненулевым типом значения блокирует сам тип значения, а не System.Nullable, который переносит значение тип.

22 голосов
/ 24 апреля 2009

В дополнение к правильному ответу Ромена, если вы хотите сравнить «реальные» типы (то есть без неявного преобразования любого обнуляемого типа в его базовый тип), вы можете создать метод расширения, например, так:

public static class MyExtensionMethods
{
    public static Type GetRealType<T>(this T source)
    {
        return typeof(T);
    }
}

А затем попробуйте следующие тесты:

int? a = 0;
Console.WriteLine(a.GetRealType() == typeof(int?));         // True
Console.WriteLine(a.GetRealType() == typeof(int));          // False

int b = 0;
Console.WriteLine(b.GetRealType() == typeof(int));          // True
Console.WriteLine(b.GetRealType() == typeof(int?));         // False

DateTime? c = DateTime.Now;
Console.WriteLine(c.GetRealType() == typeof(DateTime?));    // True
Console.WriteLine(c.GetRealType() == typeof(DateTime));     // False

DateTime d = DateTime.Now;
Console.WriteLine(d.GetRealType() == typeof(DateTime));     // True
Console.WriteLine(d.GetRealType() == typeof(DateTime?));    // False

EDIT ...

Для полноты - и подсказано комментариями SLaks ниже - вот альтернативная версия, которая использует тип времени компиляции только тогда, когда source имеет значение null или Nullable<>; в противном случае он использует GetType и возвращает тип среды выполнения:

public static class MyExtensionMethods
{
    public static Type GetRealType<T>(this T source)
    {
        Type t = typeof(T);

        if ((source == null) || (Nullable.GetUnderlyingType(t) != null))
            return t;

        return source.GetType();
    }
}
3 голосов
/ 03 апреля 2012

Хотя C # делает вид, что места хранения типов значений содержат экземпляры типов, полученных из System.ValueType, что, в свою очередь, происходит от System.Object, на самом деле это не так. Каждый тип, полученный из System.ValueType, на самом деле представляет два совершенно разных вида вещей:

  1. Набор байтов, который (для примитивных типов) представляет данные напрямую или (для не примитивных типов структуры) содержит содержимое всех полей, открытых и закрытых, но не содержит никакой информации о типах.
  2. Автономный объект кучи, который в дополнение к вышеупомянутому содержит заголовок объекта, тип которого получен из `System.ValueType`.

Места хранения типа значения содержат первое; объекты кучи типа значения содержат второй.

По разным причинам Microsoft решила, что Nullable<T> должна поддерживать только первое использование. Если вы попытаетесь передать место хранения типа Nullable<T> в код, который ожидает ссылку на объект кучи, система преобразует элемент в T, если HasValue имеет значение true, или просто передаст нулевую ссылку, если HasValue ложно. Хотя существуют способы создания объекта кучи типа Nullable<T>, обычные методы преобразования места хранения типа значения в объект кучи никогда не сгенерируют его.

Также обратите внимание, что вызов GetType() для хранилища значений не будет фактически определять тип хранилища, но вместо этого преобразует содержимое этого хранилища в объект кучи и затем возвращает тип полученного объекта. , Поскольку хранилища типа Nullable<T> преобразуются либо в экземпляры объекта T, либо в ноль, ничто в экземпляре объекта не скажет, было ли хранилище, из которого оно получено, Nullable<T>.

1 голос
/ 12 ноября 2013

Простой способ проверить, используя оператор "is":

(i is Nullable<int>) || (i is Nullable<long>) || (i is Nullable<float>) || (i is Nullable<short>)

Я понял, что прочитал эти две страницы MSDN:

http://msdn.microsoft.com/en-us/library/ms366789(v=vs.90).aspx

http://msdn.microsoft.com/en-us/library/ms228597%28v=VS.90%29.aspx

Ура!

...